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/Roslyn.sln b/Roslyn.sln index e93789c055083..704cad8e79ba5 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -585,6 +585,150 @@ Project("{9a19103f-16f7-4668-be54-9a1e7a4f7556}") = "Microsoft.CodeAnalysis.Exte 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 @@ -1427,6 +1571,222 @@ 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 @@ -1702,6 +2062,77 @@ 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} @@ -1715,6 +2146,9 @@ Global 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 @@ -1723,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 @@ -1736,15 +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 @@ -1759,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 @@ -1795,8 +2251,10 @@ 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 @@ -1806,6 +2264,7 @@ Global 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 @@ -1821,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/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/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/eng/Directory.Packages.props b/eng/Directory.Packages.props index 780303429343d..bbdb6eebfef89 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 @@ + + + + + @@ -314,5 +325,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 b0ff6e9a88c54..1de1443142a26 100644 --- a/eng/SourceBuildPrebuiltBaseline.xml +++ b/eng/SourceBuildPrebuiltBaseline.xml @@ -3,10 +3,10 @@ - + overridden by previous repo outputs or come from previously source-built artifacts. --> @@ -49,6 +49,15 @@ + + + + + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 44a5bd73fb697..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 diff --git a/eng/Versions.props b/eng/Versions.props index b22d322d4291a..3ff54c131b63b 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -92,6 +92,59 @@ 4.61.3 6.34.0 + + + + + + 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/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/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/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/AnalyzerReleases.Shipped.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000000..e69de29bb2d1d 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/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 + + + + + + +