From 48f5668da96879d8d9d0b1dbeaf6d9cfd260af57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Tue, 5 Dec 2023 11:26:55 +0100 Subject: [PATCH] Add analyzers (#1870) --- .markdownlintignore | 4 + TestFx.sln | 32 +- eng/Versions.props | 12 +- .../AssemblyResolver.cs | 2 + .../CodeFixResources.Designer.cs | 76 +++ .../CodeFixResources.resx | 124 ++++ .../MSTest.Analyzers.CodeFixes.csproj | 22 + .../UseParallelizeAttributeFixer.cs | 30 + .../xlf/CodeFixResources.cs.xlf | 12 + .../xlf/CodeFixResources.de.xlf | 12 + .../xlf/CodeFixResources.es.xlf | 12 + .../xlf/CodeFixResources.fr.xlf | 12 + .../xlf/CodeFixResources.it.xlf | 12 + .../xlf/CodeFixResources.ja.xlf | 12 + .../xlf/CodeFixResources.ko.xlf | 12 + .../xlf/CodeFixResources.pl.xlf | 12 + .../xlf/CodeFixResources.pt-BR.xlf | 12 + .../xlf/CodeFixResources.ru.xlf | 12 + .../xlf/CodeFixResources.tr.xlf | 12 + .../xlf/CodeFixResources.zh-Hans.xlf | 12 + .../xlf/CodeFixResources.zh-Hant.xlf | 12 + .../MSTest.Analyzers.Package.csproj | 45 ++ .../tools/install.ps1 | 275 +++++++++ .../tools/uninstall.ps1 | 282 ++++++++++ .../AnalyzerReleases.Shipped.md | 3 + .../AnalyzerReleases.Unshipped.md | 8 + .../MSTest.Analyzers/Helpers/Categories.cs | 9 + .../Helpers/CompilationExtensions.cs | 31 + .../MSTest.Analyzers/Helpers/DiagnosticIds.cs | 9 + .../Helpers/WellKnownCustomTags.cs | 9 + .../Helpers/WellKnownTypeNames.cs | 13 + .../MSTest.Analyzers/MSTest.Analyzers.csproj | 34 ++ .../MSTest.Analyzers/Resources.Designer.cs | 90 +++ src/Analyzers/MSTest.Analyzers/Resources.resx | 129 +++++ .../RoslynAnalyzerHelpers/.editorconfig | 32 ++ .../ArrayBuilder.Enumerator.cs | 45 ++ .../RoslynAnalyzerHelpers/ArrayBuilder.cs | 531 ++++++++++++++++++ .../BoundedCacheWithFactory.cs | 80 +++ .../ISymbolExtensions.cs | 66 +++ .../RoslynAnalyzerHelpers/ObjectPool.cs | 286 ++++++++++ .../PerformanceSensitiveAttribute.cs | 117 ++++ .../RoslynAnalyzerHelpers/PooledHashSet.cs | 93 +++ .../RoslynAnalyzerHelpers/RoslynDebug.cs | 19 + .../RoslynAnalyzerHelpers/SymbolVisibility.cs | 42 ++ .../WellKnownTypeProvider.cs | 316 +++++++++++ .../UseParallelizeAttributeAnalyzer.cs | 58 ++ .../MSTest.Analyzers/xlf/Resources.cs.xlf | 22 + .../MSTest.Analyzers/xlf/Resources.de.xlf | 22 + .../MSTest.Analyzers/xlf/Resources.es.xlf | 22 + .../MSTest.Analyzers/xlf/Resources.fr.xlf | 22 + .../MSTest.Analyzers/xlf/Resources.it.xlf | 22 + .../MSTest.Analyzers/xlf/Resources.ja.xlf | 22 + .../MSTest.Analyzers/xlf/Resources.ko.xlf | 22 + .../MSTest.Analyzers/xlf/Resources.pl.xlf | 22 + .../MSTest.Analyzers/xlf/Resources.pt-BR.xlf | 22 + .../MSTest.Analyzers/xlf/Resources.ru.xlf | 22 + .../MSTest.Analyzers/xlf/Resources.tr.xlf | 22 + .../xlf/Resources.zh-Hans.xlf | 22 + .../xlf/Resources.zh-Hant.xlf | 22 + src/Package/MSTest.Sdk/Sdk/Sdk.props.template | 8 +- .../TestFramework/xlf/Resources.cs.xlf | 22 + .../TestFramework/xlf/Resources.de.xlf | 22 + .../TestFramework/xlf/Resources.es.xlf | 22 + .../TestFramework/xlf/Resources.fr.xlf | 22 + .../TestFramework/xlf/Resources.it.xlf | 22 + .../TestFramework/xlf/Resources.ja.xlf | 22 + .../TestFramework/xlf/Resources.ko.xlf | 22 + .../TestFramework/xlf/Resources.pl.xlf | 22 + .../TestFramework/xlf/Resources.pt-BR.xlf | 22 + .../TestFramework/xlf/Resources.ru.xlf | 22 + .../TestFramework/xlf/Resources.tr.xlf | 22 + .../TestFramework/xlf/Resources.zh-Hans.xlf | 22 + .../TestFramework/xlf/Resources.zh-Hant.xlf | 22 + test/.editorconfig | 3 + .../Utilities/CLITestBase.discovery.cs | 3 - .../DeploymentTests.cs | 4 +- .../Parameterized tests/DataSourceTests.cs | 5 +- .../MSTest.Analyzers.Test.csproj | 24 + .../UseParallelizeAttributeAnalyzerTests.cs | 44 ++ .../Verifiers/CSharpCodeFixVerifier`2+Test.cs | 34 ++ .../Verifiers/CSharpCodeFixVerifier`2.cs | 63 +++ .../Verifiers/CSharpVerifierHelper.cs | 36 ++ .../Services/DesktopTestDataSourceTests.cs | 4 - .../Services/DesktopTestDeploymentTests.cs | 4 + .../Execution/TestExecutionManagerTests.cs | 9 +- .../Execution/TestMethodRunnerTests.cs | 16 +- .../Attributes/DynamicDataAttributeTests.cs | 2 + 87 files changed, 3878 insertions(+), 33 deletions(-) create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/MSTest.Analyzers.CodeFixes.csproj create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/UseParallelizeAttributeFixer.cs create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf create mode 100644 src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf create mode 100644 src/Analyzers/MSTest.Analyzers.Package/MSTest.Analyzers.Package.csproj create mode 100644 src/Analyzers/MSTest.Analyzers.Package/tools/install.ps1 create mode 100644 src/Analyzers/MSTest.Analyzers.Package/tools/uninstall.ps1 create mode 100644 src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md create mode 100644 src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md create mode 100644 src/Analyzers/MSTest.Analyzers/Helpers/Categories.cs create mode 100644 src/Analyzers/MSTest.Analyzers/Helpers/CompilationExtensions.cs create mode 100644 src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs create mode 100644 src/Analyzers/MSTest.Analyzers/Helpers/WellKnownCustomTags.cs create mode 100644 src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs create mode 100644 src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj create mode 100644 src/Analyzers/MSTest.Analyzers/Resources.Designer.cs create mode 100644 src/Analyzers/MSTest.Analyzers/Resources.resx create mode 100644 src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/.editorconfig create mode 100644 src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.Enumerator.cs create mode 100644 src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.cs create mode 100644 src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs create mode 100644 src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ISymbolExtensions.cs create mode 100644 src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ObjectPool.cs create mode 100644 src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/PerformanceSensitiveAttribute.cs create mode 100644 src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/PooledHashSet.cs create mode 100644 src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/RoslynDebug.cs create mode 100644 src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/SymbolVisibility.cs create mode 100644 src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/WellKnownTypeProvider.cs create mode 100644 src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs create mode 100644 src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf create mode 100644 src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf create mode 100644 src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf create mode 100644 src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf create mode 100644 src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf create mode 100644 src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf create mode 100644 src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf create mode 100644 src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf create mode 100644 src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf create mode 100644 src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf create mode 100644 src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf create mode 100644 src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf create mode 100644 src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf create mode 100644 src/TestFramework/TestFramework/xlf/Resources.cs.xlf create mode 100644 src/TestFramework/TestFramework/xlf/Resources.de.xlf create mode 100644 src/TestFramework/TestFramework/xlf/Resources.es.xlf create mode 100644 src/TestFramework/TestFramework/xlf/Resources.fr.xlf create mode 100644 src/TestFramework/TestFramework/xlf/Resources.it.xlf create mode 100644 src/TestFramework/TestFramework/xlf/Resources.ja.xlf create mode 100644 src/TestFramework/TestFramework/xlf/Resources.ko.xlf create mode 100644 src/TestFramework/TestFramework/xlf/Resources.pl.xlf create mode 100644 src/TestFramework/TestFramework/xlf/Resources.pt-BR.xlf create mode 100644 src/TestFramework/TestFramework/xlf/Resources.ru.xlf create mode 100644 src/TestFramework/TestFramework/xlf/Resources.tr.xlf create mode 100644 src/TestFramework/TestFramework/xlf/Resources.zh-Hans.xlf create mode 100644 src/TestFramework/TestFramework/xlf/Resources.zh-Hant.xlf create mode 100644 test/UnitTests/MSTest.Analyzers.Test/MSTest.Analyzers.Test.csproj create mode 100644 test/UnitTests/MSTest.Analyzers.Test/UseParallelizeAttributeAnalyzerTests.cs create mode 100644 test/UnitTests/MSTest.Analyzers.Test/Verifiers/CSharpCodeFixVerifier`2+Test.cs create mode 100644 test/UnitTests/MSTest.Analyzers.Test/Verifiers/CSharpCodeFixVerifier`2.cs create mode 100644 test/UnitTests/MSTest.Analyzers.Test/Verifiers/CSharpVerifierHelper.cs diff --git a/.markdownlintignore b/.markdownlintignore index 34f76156b0..c7cc1c9c8c 100644 --- a/.markdownlintignore +++ b/.markdownlintignore @@ -5,3 +5,7 @@ tools/**/*.md # Ignore release history file docs/releases.md + +# Ignore analyzer release files +src/**/AnalyzerReleases.Shipped.md +src/**/AnalyzerReleases.Unshipped.md \ No newline at end of file diff --git a/TestFx.sln b/TestFx.sln index 1e348ef8b6..204ffc536c 100644 --- a/TestFx.sln +++ b/TestFx.sln @@ -54,7 +54,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Nuget.config = Nuget.config EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4 - Package", "4 - Package", "{E374A3A6-C364-4890-B315-D60F5C682B6E}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "5 - Package", "5 - Package", "{E374A3A6-C364-4890-B315-D60F5C682B6E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTestAdapter.UnitTests", "test\UnitTests\MSTestAdapter.UnitTests\MSTestAdapter.UnitTests.csproj", "{1CEB5743-70BF-475E-91BC-0AEBD63835B7}" EndProject @@ -173,6 +173,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.Platform" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Testing.Platform.UnitTests", "test\UnitTests\Microsoft.Testing.Platform.UnitTests\Microsoft.Testing.Platform.UnitTests.csproj", "{0F1BB08E-BB6C-43E0-A7DF-1D6A03DA5DC7}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4 - Analyzers", "4 - Analyzers", "{E7F15C9C-3928-47AD-8462-64FD29FFCA54}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest.Analyzers", "src\Analyzers\MSTest.Analyzers\MSTest.Analyzers.csproj", "{72C4FA49-E553-4B39-825A-8C4EA6CE93E2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest.Analyzers.CodeFixes", "src\Analyzers\MSTest.Analyzers.CodeFixes\MSTest.Analyzers.CodeFixes.csproj", "{462B0201-1C26-4951-97C9-722C2A58EF8C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest.Analyzers.Package", "src\Analyzers\MSTest.Analyzers.Package\MSTest.Analyzers.Package.csproj", "{DC068986-7549-4B75-8EFC-A9958FD5CF88}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest.Analyzers.Test", "test\UnitTests\MSTest.Analyzers.Test\MSTest.Analyzers.Test.csproj", "{1FF35C23-C128-4C95-B3F8-67B1B4C51E4D}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSTest.Sdk", "src\Package\MSTest.Sdk\MSTest.Sdk.csproj", "{10930CFD-EDF9-4486-B0A3-49230B5A6798}" EndProject Global @@ -357,6 +366,22 @@ Global {0F1BB08E-BB6C-43E0-A7DF-1D6A03DA5DC7}.Debug|Any CPU.Build.0 = Debug|Any CPU {0F1BB08E-BB6C-43E0-A7DF-1D6A03DA5DC7}.Release|Any CPU.ActiveCfg = Release|Any CPU {0F1BB08E-BB6C-43E0-A7DF-1D6A03DA5DC7}.Release|Any CPU.Build.0 = Release|Any CPU + {72C4FA49-E553-4B39-825A-8C4EA6CE93E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72C4FA49-E553-4B39-825A-8C4EA6CE93E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72C4FA49-E553-4B39-825A-8C4EA6CE93E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72C4FA49-E553-4B39-825A-8C4EA6CE93E2}.Release|Any CPU.Build.0 = Release|Any CPU + {462B0201-1C26-4951-97C9-722C2A58EF8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {462B0201-1C26-4951-97C9-722C2A58EF8C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {462B0201-1C26-4951-97C9-722C2A58EF8C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {462B0201-1C26-4951-97C9-722C2A58EF8C}.Release|Any CPU.Build.0 = Release|Any CPU + {DC068986-7549-4B75-8EFC-A9958FD5CF88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DC068986-7549-4B75-8EFC-A9958FD5CF88}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DC068986-7549-4B75-8EFC-A9958FD5CF88}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DC068986-7549-4B75-8EFC-A9958FD5CF88}.Release|Any CPU.Build.0 = Release|Any CPU + {1FF35C23-C128-4C95-B3F8-67B1B4C51E4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1FF35C23-C128-4C95-B3F8-67B1B4C51E4D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1FF35C23-C128-4C95-B3F8-67B1B4C51E4D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1FF35C23-C128-4C95-B3F8-67B1B4C51E4D}.Release|Any CPU.Build.0 = Release|Any CPU {10930CFD-EDF9-4486-B0A3-49230B5A6798}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {10930CFD-EDF9-4486-B0A3-49230B5A6798}.Debug|Any CPU.Build.0 = Debug|Any CPU {10930CFD-EDF9-4486-B0A3-49230B5A6798}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -424,6 +449,11 @@ Global {6AEE1440-FDF0-4729-8196-B24D0E333550} = {FF8B1B72-55A1-4FFE-809E-7B79323ED8D0} {48FAB979-8DA5-492E-8B3F-5DBBE82F659A} = {6AEE1440-FDF0-4729-8196-B24D0E333550} {0F1BB08E-BB6C-43E0-A7DF-1D6A03DA5DC7} = {BB874DF1-44FE-415A-B634-A6B829107890} + {E7F15C9C-3928-47AD-8462-64FD29FFCA54} = {FF8B1B72-55A1-4FFE-809E-7B79323ED8D0} + {72C4FA49-E553-4B39-825A-8C4EA6CE93E2} = {E7F15C9C-3928-47AD-8462-64FD29FFCA54} + {462B0201-1C26-4951-97C9-722C2A58EF8C} = {E7F15C9C-3928-47AD-8462-64FD29FFCA54} + {DC068986-7549-4B75-8EFC-A9958FD5CF88} = {E7F15C9C-3928-47AD-8462-64FD29FFCA54} + {1FF35C23-C128-4C95-B3F8-67B1B4C51E4D} = {BB874DF1-44FE-415A-B634-A6B829107890} {10930CFD-EDF9-4486-B0A3-49230B5A6798} = {E374A3A6-C364-4890-B315-D60F5C682B6E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/eng/Versions.props b/eng/Versions.props index 8f95ddd9c8..7559d67b63 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -18,10 +18,9 @@ 4.3.0 4.3.1 - 6.12.0 - - 4.18.4 13.0.3 + 3.3.4 + 4.6.0 3.3.4 $(MicrosoftCodeAnalysisPublicApiAnalyzersVersion) @@ -31,6 +30,11 @@ 0.2.5 1.2.0-beta.507 1.0.0-alpha.23603.1 - 17.7.2 + + + 6.12.0 + 1.1.2-beta1.23431.1 + + 4.18.4 diff --git a/src/Adapter/MSTestAdapter.PlatformServices/AssemblyResolver.cs b/src/Adapter/MSTestAdapter.PlatformServices/AssemblyResolver.cs index a3cfce61ce..4b33214d41 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/AssemblyResolver.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/AssemblyResolver.cs @@ -401,7 +401,9 @@ private void WindowsRuntimeMetadataReflectionOnlyNamespaceResolve(object sender, /// The . [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "senderAppDomain", Justification = "This is an event handler.")] +#pragma warning disable IDE0060 // Remove unused parameter private Assembly? OnResolveInternal(object senderAppDomain, ResolveEventArgs args, bool isReflectionOnly) +#pragma warning restore IDE0060 // Remove unused parameter { if (StringEx.IsNullOrEmpty(args?.Name)) { diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs new file mode 100644 index 0000000000..d7c729179c --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.Designer.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace MSTest.Analyzers; + +using System; +using System.Reflection; + + +/// +/// A strongly-typed resource class, for looking up localized strings, etc. +/// +// This class was auto-generated by the StronglyTypedResourceBuilder +// class via a tool like ResGen or Visual Studio. +// To add or remove a member, edit your .ResX file then rerun ResGen +// with the /str option, or rebuild your VS project. +[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] +[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] +[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] +internal class CodeFixResources +{ + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CodeFixResources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if (object.ReferenceEquals(resourceMan, null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MSTest.Analyzers.CodeFixResources", typeof(CodeFixResources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Make uppercase. + /// + internal static string CodeFixTitle + { + get + { + return ResourceManager.GetString("CodeFixTitle", resourceCulture); + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx new file mode 100644 index 0000000000..97abe68945 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + Make uppercase + The title of the code fix. + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/MSTest.Analyzers.CodeFixes.csproj b/src/Analyzers/MSTest.Analyzers.CodeFixes/MSTest.Analyzers.CodeFixes.csproj new file mode 100644 index 0000000000..1941f55365 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/MSTest.Analyzers.CodeFixes.csproj @@ -0,0 +1,22 @@ + + + + netstandard2.0 + false + MSTest.Analyzers + + + + + + + + + + + + + + + + diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/UseParallelizeAttributeFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseParallelizeAttributeFixer.cs new file mode 100644 index 0000000000..82635b4e71 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseParallelizeAttributeFixer.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; +using System.Composition; +using System.Threading.Tasks; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; + +namespace MSTest.Analyzers; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(UseParallelizeAttributeFixer))] +[Shared] +public sealed class UseParallelizeAttributeFixer : CodeFixProvider +{ + public sealed override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Empty; + + public 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 override Task RegisterCodeFixesAsync(CodeFixContext context) + { + // Fixer not yet implemented. + return Task.CompletedTask; + } +} diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf new file mode 100644 index 0000000000..928af0bba3 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf @@ -0,0 +1,12 @@ + + + + + + Make uppercase + Make uppercase + The title of the code fix. + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf new file mode 100644 index 0000000000..58096d8329 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf @@ -0,0 +1,12 @@ + + + + + + Make uppercase + Make uppercase + The title of the code fix. + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf new file mode 100644 index 0000000000..f5e4b588a3 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf @@ -0,0 +1,12 @@ + + + + + + Make uppercase + Make uppercase + The title of the code fix. + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf new file mode 100644 index 0000000000..5c04e22627 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf @@ -0,0 +1,12 @@ + + + + + + Make uppercase + Make uppercase + The title of the code fix. + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf new file mode 100644 index 0000000000..7b81fb70a0 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf @@ -0,0 +1,12 @@ + + + + + + Make uppercase + Make uppercase + The title of the code fix. + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf new file mode 100644 index 0000000000..98c51048d2 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf @@ -0,0 +1,12 @@ + + + + + + Make uppercase + Make uppercase + The title of the code fix. + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf new file mode 100644 index 0000000000..b1ad7b7b21 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf @@ -0,0 +1,12 @@ + + + + + + Make uppercase + Make uppercase + The title of the code fix. + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf new file mode 100644 index 0000000000..6c230154dc --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf @@ -0,0 +1,12 @@ + + + + + + Make uppercase + Make uppercase + The title of the code fix. + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf new file mode 100644 index 0000000000..834d748d6d --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf @@ -0,0 +1,12 @@ + + + + + + Make uppercase + Make uppercase + The title of the code fix. + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf new file mode 100644 index 0000000000..d30b3746c8 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf @@ -0,0 +1,12 @@ + + + + + + Make uppercase + Make uppercase + The title of the code fix. + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf new file mode 100644 index 0000000000..ac09308b6f --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf @@ -0,0 +1,12 @@ + + + + + + Make uppercase + Make uppercase + The title of the code fix. + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf new file mode 100644 index 0000000000..6565df44bb --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf @@ -0,0 +1,12 @@ + + + + + + Make uppercase + Make uppercase + The title of the code fix. + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf new file mode 100644 index 0000000000..407bd05b0d --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf @@ -0,0 +1,12 @@ + + + + + + Make uppercase + Make uppercase + The title of the code fix. + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers.Package/MSTest.Analyzers.Package.csproj b/src/Analyzers/MSTest.Analyzers.Package/MSTest.Analyzers.Package.csproj new file mode 100644 index 0000000000..dff0d01721 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.Package/MSTest.Analyzers.Package.csproj @@ -0,0 +1,45 @@ + + + + netstandard2.0 + false + true + true + + + + MSTest.Analyzers + 1.0.0.0 + amauryleve + http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE + http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE + http://ICON_URL_HERE_OR_DELETE_THIS_LINE + http://REPOSITORY_URL_HERE_OR_DELETE_THIS_LINE + false + MSTest.Analyzers + Summary of changes made in this release of the package. + Copyright + MSTest.Analyzers, analyzers + true + true + + $(TargetsForTfmSpecificContentInPackage);_AddAnalyzersToOutput + + + + + + + + + + + + + + + + + + + diff --git a/src/Analyzers/MSTest.Analyzers.Package/tools/install.ps1 b/src/Analyzers/MSTest.Analyzers.Package/tools/install.ps1 new file mode 100644 index 0000000000..53136a238f --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.Package/tools/install.ps1 @@ -0,0 +1,275 @@ +param($installPath, $toolsPath, $package, $project) + +if($project.Object.SupportsPackageDependencyResolution) +{ + if($project.Object.SupportsPackageDependencyResolution()) + { + # Do not install analyzers via install.ps1, instead let the project system handle it. + return + } +} + +$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve + +foreach($analyzersPath in $analyzersPaths) +{ + if (Test-Path $analyzersPath) + { + # Install the language agnostic analyzers. + foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.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 -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll) + { + if($project.Object.AnalyzerReferences) + { + $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) + } + } + } +} +# SIG # Begin signature block +# MIIoKgYJKoZIhvcNAQcCoIIoGzCCKBcCAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA/i+qRUHsWzI0s +# FVk99zLgt/HOEQ33uvkFsWtHTHZgf6CCDXYwggX0MIID3KADAgECAhMzAAADTrU8 +# esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD +# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy +# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p +# bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw +# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u +# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy +# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +# AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU +# p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1 +# 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm +# WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa +# +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq +# jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE +# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw +# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW +# MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci +# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j +# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG +# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu +# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 +# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk +# mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31 +# TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2 +# kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d +# hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM +# pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh +# JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX +# UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir +# IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8 +# 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A +# Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H +# tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq +# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x +# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv +# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG +# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG +# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg +# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 +# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr +# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg +# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy +# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 +# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh +# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k +# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB +# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn +# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 +# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w +# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o +# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD +# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa +# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny +# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG +# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t +# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV +# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 +# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG +# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl +# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb +# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l +# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 +# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 +# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 +# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam +# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa +# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah +# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA +# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt +# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr +# /Xmfwb1tbWrJUnMTDXpQzTGCGgowghoGAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw +# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN +# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp +# Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB +# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO +# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIEY4Ow3COroWH11sAEOoStJj +# 1u4sq9rcx0dAx0gyZLHCMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A +# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB +# BQAEggEAFlSLlk17KQp2AwLbr5e4T5zyE44MGZOdNejIip+Mg8co8qho16qdNSvg +# AhvoJYxPJJr70DSCU9BIUoWY7hXoi9S4P08YlAid7BUT5OciIgHlrb8I900LaE+S +# 83rSvfVU1CZCjiSwcgng5DD2VPRo0Lu4G9p2Ky14dyOPwtPvrpsib5s9kewZLdiy +# /KxEDLKX8P+cHat1xH7RaZLDNxweRS6GSomjE2vjOlQHNSW879XR8bSoAt/m4uR1 +# WyrAxTGZb4miEYX+I5HsrWvbZLw9NSCJ/crbbap3LIobfQtK5binjY7v4MQp/5Oq +# y4S/4FAfwhWDXfaQfq6YTeOjHRVQbKGCF5QwgheQBgorBgEEAYI3AwMBMYIXgDCC +# F3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq +# hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl +# AwQCAQUABCCV3irbWIoYz2Llfx9YQhUNtcP6GOrL7+YTUXQ4y5qzWAIGZNTJrsAW +# GBMyMDIzMDgzMTAwMTI1OC45ODNaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV +# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE +# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l +# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTIwMC0w +# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg +# ghHqMIIHIDCCBQigAwIBAgITMwAAAc9SNr5xS81IygABAAABzzANBgkqhkiG9w0B +# AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE +# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD +# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzA1MjUxOTEy +# MTFaFw0yNDAyMDExOTEyMTFaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz +# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv +# cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z +# MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTIwMC0wNUUwLUQ5NDcxJTAjBgNV +# BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB +# AQUAA4ICDwAwggIKAoICAQC4Pct+15TYyrUje553lzBQodgmd5Bz7WuH8SdHpAoW +# z+01TrHExBSuaMKnxvVMsyYtas5h6aopUGAS5WKVLZAvUtH62TKmAE0JK+i1hafi +# CSXLZPcRexxeRkOqeZefLBzXp0nudMOXUUab333Ss8LkoK4l3LYxm1Ebsr3b2OTo +# 2ebsAoNJ4kSxmVuPM7C+RDhGtVKR/EmHsQ9GcwGmluu54bqiVFd0oAFBbw4txTU1 +# mruIGWP/i+sgiNqvdV/wah/QcrKiGlpWiOr9a5aGrJaPSQD2xgEDdPbrSflYxsRM +# dZCJI8vzvOv6BluPcPPGGVLEaU7OszdYjK5f4Z5Su/lPK1eST5PC4RFsVcOiS4L0 +# sI4IFZywIdDJHoKgdqWRp6Q5vEDk8kvZz6HWFnYLOlHuqMEYvQLr6OgooYU9z0A5 +# cMLHEIHYV1xiaBzx2ERiRY9MUPWohh+TpZWEUZlUm/q9anXVRN0ujejm6OsUVFDs +# sIMszRNCqEotJGwtHHm5xrCKuJkFr8GfwNelFl+XDoHXrQYL9zY7Np+frsTXQpKR +# NnmI1ashcn5EC+wxUt/EZIskWzewEft0/+/0g3+8YtMkUdaQE5+8e7C8UMiXOHkM +# K25jNNQqLCedlJwFIf9ir9SpMc72NR+1j6Uebiz/ZPV74do3jdVvq7DiPFlTb92U +# KwIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFDaeKPtp0eTSVdG+gZc5BDkabTg4MB8G +# A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG +# Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy +# MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w +# XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy +# dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG +# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD +# AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQBQgm4pnA0xkd/9uKXJMzdMYyxUfUm/ZusU +# Ba32MEZXQuMGp20pSuX2VW9/tpTMo5bkaJdBVoUyd2DbDsNb1kjr/36ntT0jvL3A +# oWStAFhZBypmpPbx+BPK49ZlejlM4d5epX668tRRGfFip9Til9yKRfXBrXnM/q64 +# IinN7zXEQ3FFQhdJMzt8ibXClO7eFA+1HiwZPWysYWPb/ZOFobPEMvXie+GmEbTK +# bhE5tze6RrA9aejjP+v1ouFoD5bMj5Qg+wfZXqe+hfYKpMd8QOnQyez+Nlj1ityn +# OZWfwHVR7dVwV0yLSlPT+yHIO8g+3fWiAwpoO17bDcntSZ7YOBljXrIgad4W4gX+ +# 4tp1eBsc6XWIITPBNzxQDZZRxD4rXzOB6XRlEVJdYZQ8gbXOirg/dNvS2GxcR50Q +# dOXDAumdEHaGNHb6y2InJadCPp2iT5QLC4MnzR+YZno1b8mWpCdOdRs9g21QbbrI +# 06iLk9KD61nx7K5ReSucuS5Z9nbkIBaLUxDesFhr1wmd1ynf0HQ51Swryh7YI7TX +# T0jr81mbvvI9xtoqjFvIhNBsICdCfTR91ylJTH8WtUlpDhEgSqWt3gzNLPTSvXAx +# XTpIM583sZdd+/2YGADMeWmt8PuMce6GsIcLCOF2NiYZ10SXHZS5HRrLrChuzedD +# RisWpIu5uTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI +# hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw +# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x +# MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy +# MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC +# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV +# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp +# bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +# AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg +# M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF +# dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6 +# GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp +# Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu +# yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E +# XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0 +# lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q +# GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ +# +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA +# PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw +# EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG +# NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV +# MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj +# cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK +# BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC +# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX +# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v +# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI +# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j +# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG +# 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x +# M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC +# VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449 +# xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM +# nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS +# PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d +# Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn +# GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs +# QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL +# jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL +# 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNN +# MIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp +# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw +# b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn +# MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjkyMDAtMDVFMC1EOTQ3MSUwIwYDVQQD +# ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQDq +# 8xzVXwLguauAQj1rrJ4/TyEMm6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD +# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy +# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w +# IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6JpNsjAiGA8yMDIzMDgzMDIzMjIy +# NloYDzIwMjMwODMxMjMyMjI2WjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDomk2y +# AgEAMAcCAQACAi7oMAcCAQACAhLnMAoCBQDom58yAgEAMDYGCisGAQQBhFkKBAIx +# KDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZI +# hvcNAQELBQADggEBADE16CHIs3WeXYKQG9djja+StAB7gsNJxV9p+CETL3847UL3 +# +DIeoj6p5g0FkS8PJK2xc+UbcNx6XJO+WUXEbU9GL4mrOCQjYOM+i3r8FEU+l3Gh +# 6ZG/9ygsIYRXEfDKK4lsbrrUkFQs9nDISHT3f8JZEJJXSsGmwcHWlNC0LC8bv0Jp +# e2Bw2+SNc6SlGD8Vv45r4WFPHhfSioCz4HSsF1He3/2Wku7OH85FKvugBlsca7+F +# bpGsDSL4LO9bv60DxuD+8xBZuyTB8s64ifCGlOXCNpK5VaHND48PhoJbuD0COwlM +# Rn5NlT6T4hhtkPOqNscMlzYHmTOKc5NhWK8PyrIxggQNMIIECQIBATCBkzB8MQsw +# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u +# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy +# b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAc9SNr5xS81IygABAAABzzAN +# BglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8G +# CSqGSIb3DQEJBDEiBCAjGiC3/PscCMBvPZjpZqbYcL2WRZ+Ecf74oiIPQKkjSzCB +# +gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EILPpsLqeNS4NuYXE2VJlMuvQeWVA +# 80ZDFhpOPjSzhPa/MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh +# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD +# b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw +# MTACEzMAAAHPUja+cUvNSMoAAQAAAc8wIgQgWdbLA3Co0zf3nE5086HU0tdn0vK7 +# yXyU9aAFpszvWFIwDQYJKoZIhvcNAQELBQAEggIANrHN06oaP0N1lUSxxoJteJgI +# fQjN82xV5VSE0cwNCBy05fg1VydPERF59+MIwIlJHhikSo3YOj5tt3AohC78U46P +# 2IdrLHKlA3rjiLUGwQvGUKJUSsEH4uueA1mmh9jwUBJhY3NjTcMQaQqp/oxZTaya +# l7NqbubBIZvsDD126SUr8jtVWtZZzw8pnWCFb4Rijii4fY1UiQzfQLFwqQuid6tE +# I0AaY3IoTlp7U9K2wfAPWcP1G7n3qv+990GEiQlGJlCfIJSSJQodzL2QZF5HCn/K +# SfgkRPn3y/Aax8683mWCT8zricYzO3MZ9j0T7tAcqOiWb7PFCsk5Va44lq4Gv0u+ +# +60FYCAA/Qn6eMuNkqIpBeIK0+NYUcMSwPdY/riKXdgkVLwChEJC5WWznD/Iqsil +# Jj9XYailhXzYx7Pa2MLays62LPwCnUxRQBTD9/rL3XQMj69iA4lisb51dKAtrqAU +# 53aRXFn+twYYFTAUQ/oNK14nZEQE5H53xAfhphMokJOu+CnQQKeCMeYlex6Q4zfw +# TQxP/xXZ+QW2cSZTwh1d5iE0XMhKCZxhxIOF/rRA+75L5GUz60reRZPeH/7USYZL +# VPc0+kxIdSbNFJAhAp0u59wSMQdBofor3+HfDfmxmoSjfCSH4TvOkNIulX1PPJPX +# UB7H4n7XHWJPkUU0cUw= +# SIG # End signature block diff --git a/src/Analyzers/MSTest.Analyzers.Package/tools/uninstall.ps1 b/src/Analyzers/MSTest.Analyzers.Package/tools/uninstall.ps1 new file mode 100644 index 0000000000..1f6b6d4f0e --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers.Package/tools/uninstall.ps1 @@ -0,0 +1,282 @@ +param($installPath, $toolsPath, $package, $project) + +if($project.Object.SupportsPackageDependencyResolution) +{ + if($project.Object.SupportsPackageDependencyResolution()) + { + # Do not uninstall analyzers via uninstall.ps1, instead let the project system handle it. + return + } +} + +$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve + +foreach($analyzersPath in $analyzersPaths) +{ + # Uninstall the language agnostic analyzers. + if (Test-Path $analyzersPath) + { + foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.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 -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll) + { + if($project.Object.AnalyzerReferences) + { + try + { + $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) + } + catch + { + + } + } + } + } +} +# SIG # Begin signature block +# MIIoLQYJKoZIhvcNAQcCoIIoHjCCKBoCAQExDzANBglghkgBZQMEAgEFADB5Bgor +# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG +# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDC68wb97fg0QGL +# yXrxJhYfmibzcOh8caqC0uZprfczDaCCDXYwggX0MIID3KADAgECAhMzAAADTrU8 +# esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD +# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy +# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p +# bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw +# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u +# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy +# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +# AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU +# p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1 +# 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm +# WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa +# +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq +# jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE +# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw +# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW +# MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci +# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j +# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG +# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu +# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 +# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk +# mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31 +# TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2 +# kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d +# hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM +# pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh +# JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX +# UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir +# IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8 +# 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A +# Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H +# tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq +# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x +# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv +# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG +# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG +# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg +# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 +# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr +# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg +# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy +# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 +# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh +# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k +# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB +# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn +# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 +# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w +# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o +# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD +# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa +# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny +# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG +# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t +# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV +# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 +# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG +# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl +# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb +# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l +# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 +# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 +# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 +# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam +# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa +# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah +# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA +# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt +# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr +# /Xmfwb1tbWrJUnMTDXpQzTGCGg0wghoJAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw +# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN +# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp +# Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB +# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO +# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIBdcqRcs5QL71hlQnl3M636V +# 5iTZvb6co3MHeMuIr36qMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A +# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB +# BQAEggEARbhO/zqm6SWBf6DSJ3P1r82VjSyMGXaLtMfTOq/bqHOXPOqC25R1v5uO +# zu4ri+UOS8dU6EfW6C9Xf/Z1Ue/oxrvxn5j8mPvsmcs5OyDO0hW0Wv6pYEy5Z5up +# mfcqvfUfl3+ir29lgPuz0f1mLpz0XxqhjqElEi5RfZD1k1YVg65f0qHroP2txql5 +# TfC77DXJ8verzVm1wqXBHTAERQD94TJobahYTCmyaudMjLUVFakv2lTMv0YTnrQR +# So006ZQg3i1jcVCJt/bRDGKh3xUo1IHgoh3NjMEkxT3iWt8rnX8Us6T6Zg8B2OxC +# 0EnuIu/eYYUlYTLQxO9eks7w1kVcUqGCF5cwgheTBgorBgEEAYI3AwMBMYIXgzCC +# F38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq +# hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl +# AwQCAQUABCBQ1HvJFFsT0ICl6oEZlSNN8DlUeQobBHt/oTUGN6iKBgIGZNTKS0Bc +# GBMyMDIzMDgzMTAwMTI1OC4zODVaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV +# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE +# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l +# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046N0YwMC0w +# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg +# ghHtMIIHIDCCBQigAwIBAgITMwAAAdWpAs/Fp8npWgABAAAB1TANBgkqhkiG9w0B +# AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE +# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD +# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzA1MjUxOTEy +# MzBaFw0yNDAyMDExOTEyMzBaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz +# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv +# cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z +# MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046N0YwMC0wNUUwLUQ5NDcxJTAjBgNV +# BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB +# AQUAA4ICDwAwggIKAoICAQDFfak57Oph9vuxtloABiLc6enT+yKH619b+OhGdkyh +# gNzkX80KUGI/jEqOVMV4Sqt/UPFFidx2t7v2SETj2tAzuVKtDfq2HBpu80vZ0vyQ +# DydVt4MDL4tJSKqgYofCxDIBrWzJJjgBolKdOJx1ut2TyOc+UOm7e92tVPHpjdg+ +# Omf31TLUf/oouyAOJ/Inn2ih3ASP0QYm+AFQjhYDNDu8uzMdwHF5QdwsscNa9PVS +# GedLdDLo9jL6DoPF4NYo06lvvEQuSJ9ImwZfBGLy/8hpE7RD4ewvJKmM1+t6eQuE +# sTXjrGM2WjkW18SgUZ8n+VpL2uk6AhDkCa355I531p0Jkqpoon7dHuLUdZSQO40q +# mVIQ6qQCanvImTqmNgE/rPJ0rgr0hMPI/uR1T/iaL0mEq4bqak+3sa8I+FAYOI/P +# C7V+zEek+sdyWtaX+ndbGlv/RJb5mQaGn8NunbkfvHD1Qt5D0rmtMOekYMq7QjYq +# E3FEP/wAY4TDuJxstjsa2HXi2yUDEg4MJL6/JvsQXToOZ+IxR6KT5t5fB5FpZYBp +# VLMma3pm5z6VXvkXrYs33NXJqVWLwiswa7NUFV87Es2sou9Idw3yAZmHIYWgOQ+D +# IY1nY3aG5DODiwN1rJyEb+mbWDagrdVxcncr6UKKO49eoNTXEW+scUf6GwXG0KEy +# mQIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFK/QXKNO35bBMOz3R5giX7Ala2OaMB8G +# A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG +# Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy +# MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w +# XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy +# dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG +# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD +# AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQBmRddqvQuyjRpx0HGxvOqffFrbgFAg0j82 +# v0v7R+/8a70S2V4t7yKYKSsQGI6pvt1A8JGmuZyjmIXmw23AkI5bZkxvSgws8rrB +# tJw9vakEckcWFQb7JG6b618x0s9Q3DL0dRq46QZRnm7U6234lecvjstAow30dP0T +# nIacPWKpPc3QgB+WDnglN2fdT1ruQ6WIVBenmpjpG9ypRANKUx5NRcpdJAQW2FqE +# HTS3Ntb+0tCqIkNHJ5aFsF6ehRovWZp0MYIz9bpJHix0VrjdLVMOpe7wv62t90E3 +# UrE2KmVwpQ5wsMD6YUscoCsSRQZrA5AbwTOCZJpeG2z3vDo/huvPK8TeTJ2Ltu/I +# tXgxIlIOQp/tbHAiN8Xptw/JmIZg9edQ/FiDaIIwG5YHsfm2u7TwOFyd6OqLw18Z +# 5j/IvDPzlkwWJxk6RHJF5dS4s3fnyLw3DHBe5Dav6KYB4n8x/cEmD/R44/8gS5Pf +# uG1srjLdyyGtyh0KiRDSmjw+fa7i1VPoemidDWNZ7ksNadMad4ZoDvgkqOV4A6a+ +# N8HIc/P6g0irrezLWUgbKXSN8iH9RP+WJFx5fBHE4AFxrbAUQ2Zn5jDmHAI3wYcQ +# DnnEYP51A75WFwPsvBrfrb1+6a1fuTEH1AYdOOMy8fX8xKo0E0Ys+7bxIvFPsUpS +# zfFjBolmhzCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI +# hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw +# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x +# MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy +# MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC +# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV +# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp +# bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +# AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg +# M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF +# dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6 +# GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp +# Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu +# yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E +# XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0 +# lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q +# GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ +# +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA +# PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw +# EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG +# NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV +# MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj +# cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK +# BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC +# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX +# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v +# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI +# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j +# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG +# 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x +# M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC +# VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449 +# xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM +# nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS +# PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d +# Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn +# GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs +# QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL +# jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL +# 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNQ +# MIICOAIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp +# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw +# b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn +# MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjdGMDAtMDVFMC1EOTQ3MSUwIwYDVQQD +# ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQBO +# Ei+S/ZVFe6w1Id31m6Kge26lNKCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD +# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy +# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w +# IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6JpN3jAiGA8yMDIzMDgzMDIzMjMx +# MFoYDzIwMjMwODMxMjMyMzEwWjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDomk3e +# AgEAMAoCAQACAiLLAgH/MAcCAQACAhLTMAoCBQDom59eAgEAMDYGCisGAQQBhFkK +# BAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJ +# KoZIhvcNAQELBQADggEBAC/zAZ9IEqD3nHBUWwxFTfnDSRErnCJ6XGxH5jOVtR0t +# 9pi7yCpApLpA2D12g1lcv4ugnwGpsbwmFjrcF4WlHemRa77qv409xNhNKrnh3H+U +# X2hvy9Utp9LiJiqS7lOW5VN1Uv+LbnA+FWt//4J+YLv44D/dliUGjYX623X7KiEX +# dbdXPR/Sn+W2YVQ19O8liKaFDnDnIAz+WLCfL6EaoGu4Te/Mr65Khy3YWTwQfXxr +# gR/JMDzLzWossnGszYCN8S8d9X6mfzWuYv4JHLEiThW++WbMLeT2hhKPomcbvqU4 +# wPb/ylDrTrWuAr/fVndECXVjCIzYJiFwOWn/ZfN9FpQxggQNMIIECQIBATCBkzB8 +# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk +# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N +# aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAdWpAs/Fp8npWgABAAAB +# 1TANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE +# MC8GCSqGSIb3DQEJBDEiBCAHS+EjBkJ/YPugQQv1D7eXVQOI4DWPIYpUxpssheDN +# lTCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EINm/I4YM166JMM7EKIcYvlcb +# r2CHjKC0LUOmpZIbBsH/MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m +# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB +# IDIwMTACEzMAAAHVqQLPxafJ6VoAAQAAAdUwIgQgBVj4mNvMkXn0xEeEe3i6urr/ +# L5Eh2p4uPk3zOcR/jHIwDQYJKoZIhvcNAQELBQAEggIAiKbnM5VQAwi5gWDevpEJ +# oHiZRrb0HApEUkca54Oye2+5tyRfWo3ruZvLZueHWsbEzYXclOscHVPjyR1t6s+p +# W13utx4mDab1BaJCBq1q9IH1HDfCf/kSMb0H02UDNBumrt1s5/oaiiY75S3kigpt +# XHCKV/AKbi+4vORYdBwJhWvy4kug+zZHWAp7+K4rVeAaMbWOitriFPQaSdZlOkNT +# VKMbYUtX6VEnhyfr5O390TxPJCdkDznT6rAf5FlQEkim7wb2gb/Osi73KF0dtnRd +# 0ePNT4GxulYTrAhHsZmyuinh/FIyqJDtW3C+2eVt/lx6GJfAtDA/gVCe9mAL4bYv +# 7vjocHeLvWJ8bc0PYMCRZXMsU2zkJqVJ7pZWq+z5hGQAHemrQj9hUZBXCEFVZ8dd +# jEZbIsg2nwZOFakhvdAvvGlTXPbRMOCHblToXAKA8ksRbLob8CDL6Cstoy5VL3al +# fOAWLj3FYITlvGvAYCtJzHrHdqyphL805Co1syR6YDopR8tDrxgWzJKAby5fIolP +# 7SfunXsCa0n3xx80aaxR0apljIXZWBSMGZdJmVzASQFxlexNsU1PmaLmmpn0fih/ +# 567e+kyzFG1wiw5btwttSW9hKgCX+yze1B4IK4yquTQSfPikrCpbBaFZ0OlWmOnL +# r0eKAKY3WDyxYgNyeg6KeCc= +# SIG # End signature block diff --git a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000..60b59dd99b --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Shipped.md @@ -0,0 +1,3 @@ +; Shipped analyzer releases +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + diff --git a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000..ea1e9ddc08 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md @@ -0,0 +1,8 @@ +; Unshipped analyzer release +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +MSTEST0001 | Performance | Warning | UseParallelizeAttributeAnalyzer \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/Categories.cs b/src/Analyzers/MSTest.Analyzers/Helpers/Categories.cs new file mode 100644 index 0000000000..91b6103349 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/Helpers/Categories.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace MSTest.Analyzers; + +internal static class Categories +{ + public const string Performance = nameof(Performance); +} diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/CompilationExtensions.cs b/src/Analyzers/MSTest.Analyzers/Helpers/CompilationExtensions.cs new file mode 100644 index 0000000000..58e039cce2 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/Helpers/CompilationExtensions.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; + +using Analyzer.Utilities; + +using Microsoft.CodeAnalysis; + +namespace MSTest.Analyzers.Helpers; + +internal static class CompilationExtensions +{ + /// + /// 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); +} diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs new file mode 100644 index 0000000000..5a772bb084 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace MSTest.Analyzers; + +internal static class DiagnosticIds +{ + public const string UseParallelizedAttributeRuleId = "MSTEST0001"; +} diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownCustomTags.cs b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownCustomTags.cs new file mode 100644 index 0000000000..b7e30efb3d --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownCustomTags.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace MSTest.Analyzers.Helpers; + +internal static class WellKnownCustomTags +{ + public const string CompilationEnd = nameof(CompilationEnd); +} diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs new file mode 100644 index 0000000000..f517c295aa --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace MSTest.Analyzers; + +// IMPORTANT: Keep this file sorted alphabetically. +internal static class WellKnownTypeNames +{ + public const string MicrosoftVisualStudioTestToolsUnitTestingParallelizeAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.ParallelizeAttribute"; + public const string MicrosoftVisualStudioTestToolsUnitTestingDoNotParallelizeAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.DoNotParallelizeAttribute"; + + public const string SystemThreadingTasksTask1 = "System.Threading.Tasks.Task`1"; +} diff --git a/src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj b/src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj new file mode 100644 index 0000000000..7d2a265def --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/MSTest.Analyzers.csproj @@ -0,0 +1,34 @@ + + + + netstandard2.0 + false + true + enable + Latest + true + + + *$(MSBuildProjectFile)* + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs b/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs new file mode 100644 index 0000000000..1756cd57ac --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace MSTest.Analyzers { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MSTest.Analyzers.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'.. + /// + internal static string UseParallelizeAttributeAnalyzerDescription { + get { + return ResourceManager.GetString("UseParallelizeAttributeAnalyzerDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Explicitely enable or disable tests parallelization. + /// + internal static string UseParallelizeAttributeAnalyzerMessageFormat { + get { + return ResourceManager.GetString("UseParallelizeAttributeAnalyzerMessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable tests parallelization. + /// + internal static string UseParallelizeAttributeAnalyzerTitle { + get { + return ResourceManager.GetString("UseParallelizeAttributeAnalyzerTitle", resourceCulture); + } + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/Resources.resx b/src/Analyzers/MSTest.Analyzers/Resources.resx new file mode 100644 index 0000000000..45b8d63f52 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/Resources.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + + + Explicitely enable or disable tests parallelization + + + Enable tests parallelization + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/.editorconfig b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/.editorconfig new file mode 100644 index 0000000000..cbd58c5433 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/.editorconfig @@ -0,0 +1,32 @@ +root = false + +[*.cs] + +dotnet_diagnostic.IDE0003.severity = none +dotnet_diagnostic.IDE0005.severity = none +dotnet_diagnostic.IDE0045.severity = none +dotnet_diagnostic.IDE0046.severity = none +dotnet_diagnostic.IDE0073.severity = none +dotnet_diagnostic.IDE0161.severity = none +dotnet_diagnostic.IDE1006.severity = none + +dotnet_diagnostic.SA1025.severity = none +dotnet_diagnostic.SA1028.severity = none +dotnet_diagnostic.SA1108.severity = none +dotnet_diagnostic.SA1116.severity = none +dotnet_diagnostic.SA1127.severity = none +dotnet_diagnostic.SA1201.severity = none +dotnet_diagnostic.SA1214.severity = none +dotnet_diagnostic.SA1311.severity = none +dotnet_diagnostic.SA1314.severity = none +dotnet_diagnostic.SA1405.severity = none +dotnet_diagnostic.SA1502.severity = none +dotnet_diagnostic.SA1512.severity = none +dotnet_diagnostic.SA1516.severity = none +dotnet_diagnostic.SA1602.severity = none +dotnet_diagnostic.SA1604.severity = none +dotnet_diagnostic.SA1618.severity = none +dotnet_diagnostic.SA1629.severity = none +dotnet_diagnostic.SA1636.severity = none + +dotnet_diagnostic.RS1035.severity = none \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.Enumerator.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.Enumerator.cs new file mode 100644 index 0000000000..f4d5f6dabb --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.Enumerator.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license 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 => Current; + + public void Reset() + { + _index = -1; + } + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.cs new file mode 100644 index 0000000000..7f5657017f --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ArrayBuilder.cs @@ -0,0 +1,531 @@ +// 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.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, Count, match); + + public int FindIndex(int startIndex, Predicate match) + => FindIndex(startIndex, 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 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(); + } + + Free(); + return result; + } + + public T[] ToArrayAndFree() + { + var result = ToArray(); + 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 (Count != 0) + { + 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 (Count == 1) + { + var dictionary1 = new Dictionary>(1, comparer); + T value = this[0]; + dictionary1.Add(keySelector(value), ImmutableArray.Create(value)); + return dictionary1; + } + + if (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/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs new file mode 100644 index 0000000000..97201ec5a5 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/BoundedCacheWithFactory.cs @@ -0,0 +1,80 @@ +// 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; + +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/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ISymbolExtensions.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ISymbolExtensions.cs new file mode 100644 index 0000000000..01bc848819 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ISymbolExtensions.cs @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license 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 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; + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ObjectPool.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ObjectPool.cs new file mode 100644 index 0000000000..ced19d66fa --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/ObjectPool.cs @@ -0,0 +1,286 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license 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/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/PerformanceSensitiveAttribute.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/PerformanceSensitiveAttribute.cs new file mode 100644 index 0000000000..2f753bdefb --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/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/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/PooledHashSet.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/PooledHashSet.cs new file mode 100644 index 0000000000..1ee9b0dbd5 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/PooledHashSet.cs @@ -0,0 +1,93 @@ +// 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.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; + } + + Clear(); + _pool?.Free(this, cancellationToken); + } + + public ImmutableHashSet ToImmutableAndFree() + { + ImmutableHashSet result; + if (Count == 0) + { + result = ImmutableHashSet.Empty; + } + else + { + result = this.ToImmutableHashSet(Comparer); + 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/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/RoslynDebug.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/RoslynDebug.cs new file mode 100644 index 0000000000..8488921e8a --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/RoslynDebug.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license 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/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/SymbolVisibility.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/SymbolVisibility.cs new file mode 100644 index 0000000000..3fc1331e85 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/SymbolVisibility.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license 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/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/WellKnownTypeProvider.cs b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/WellKnownTypeProvider.cs new file mode 100644 index 0000000000..b08c0633a7 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/RoslynAnalyzerHelpers/WellKnownTypeProvider.cs @@ -0,0 +1,316 @@ +// 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.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 MSTest.Analyzers; + +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/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs new file mode 100644 index 0000000000..81918534ba --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public sealed class UseParallelizeAttributeAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.UseParallelizeAttributeAnalyzerTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.UseParallelizeAttributeAnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString Description = new(nameof(Resources.UseParallelizeAttributeAnalyzerDescription), Resources.ResourceManager, typeof(Resources)); + internal static readonly DiagnosticDescriptor Rule = new(DiagnosticIds.UseParallelizedAttributeRuleId, Title, MessageFormat, Categories.Performance, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description, customTags: WellKnownCustomTags.CompilationEnd); + + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationAction(AnalyzeCompilation); + } + + private static void AnalyzeCompilation(CompilationAnalysisContext context) + { + var parallelizeAttributeSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingParallelizeAttribute); + var doNotParallelizeAttributeSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingDoNotParallelizeAttribute); + + bool hasParallelizeAttribute = false; + bool hasDoNotParallelizeAttribute = false; + foreach (var attribute in context.Compilation.Assembly.GetAttributes()) + { + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, parallelizeAttributeSymbol)) + { + hasParallelizeAttribute = true; + } + + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, doNotParallelizeAttributeSymbol)) + { + hasDoNotParallelizeAttribute = true; + } + } + + if (!hasParallelizeAttribute && !hasDoNotParallelizeAttribute) + { + // We cannot provide any good location for assembly level missing attributes + context.ReportDiagnostic(Diagnostic.Create(Rule, Location.None)); + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf new file mode 100644 index 0000000000..6eac3c1e30 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf @@ -0,0 +1,22 @@ + + + + + + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + + + + Explicitely enable or disable tests parallelization + Explicitely enable or disable tests parallelization + + + + Enable tests parallelization + Enable tests parallelization + + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf new file mode 100644 index 0000000000..48e20a8b8b --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf @@ -0,0 +1,22 @@ + + + + + + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + + + + Explicitely enable or disable tests parallelization + Explicitely enable or disable tests parallelization + + + + Enable tests parallelization + Enable tests parallelization + + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf new file mode 100644 index 0000000000..13b4bc1c21 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf @@ -0,0 +1,22 @@ + + + + + + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + + + + Explicitely enable or disable tests parallelization + Explicitely enable or disable tests parallelization + + + + Enable tests parallelization + Enable tests parallelization + + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf new file mode 100644 index 0000000000..c80a7c3786 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf @@ -0,0 +1,22 @@ + + + + + + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + + + + Explicitely enable or disable tests parallelization + Explicitely enable or disable tests parallelization + + + + Enable tests parallelization + Enable tests parallelization + + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf new file mode 100644 index 0000000000..2b29bd792b --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf @@ -0,0 +1,22 @@ + + + + + + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + + + + Explicitely enable or disable tests parallelization + Explicitely enable or disable tests parallelization + + + + Enable tests parallelization + Enable tests parallelization + + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf new file mode 100644 index 0000000000..20125c1fc5 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf @@ -0,0 +1,22 @@ + + + + + + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + + + + Explicitely enable or disable tests parallelization + Explicitely enable or disable tests parallelization + + + + Enable tests parallelization + Enable tests parallelization + + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf new file mode 100644 index 0000000000..17a5bd732a --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf @@ -0,0 +1,22 @@ + + + + + + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + + + + Explicitely enable or disable tests parallelization + Explicitely enable or disable tests parallelization + + + + Enable tests parallelization + Enable tests parallelization + + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf new file mode 100644 index 0000000000..3f5464793b --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf @@ -0,0 +1,22 @@ + + + + + + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + + + + Explicitely enable or disable tests parallelization + Explicitely enable or disable tests parallelization + + + + Enable tests parallelization + Enable tests parallelization + + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf new file mode 100644 index 0000000000..a75fcce87a --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf @@ -0,0 +1,22 @@ + + + + + + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + + + + Explicitely enable or disable tests parallelization + Explicitely enable or disable tests parallelization + + + + Enable tests parallelization + Enable tests parallelization + + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf new file mode 100644 index 0000000000..4718a195d3 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf @@ -0,0 +1,22 @@ + + + + + + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + + + + Explicitely enable or disable tests parallelization + Explicitely enable or disable tests parallelization + + + + Enable tests parallelization + Enable tests parallelization + + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf new file mode 100644 index 0000000000..afa9d3b24b --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf @@ -0,0 +1,22 @@ + + + + + + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + + + + Explicitely enable or disable tests parallelization + Explicitely enable or disable tests parallelization + + + + Enable tests parallelization + Enable tests parallelization + + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf new file mode 100644 index 0000000000..bcb102eb9c --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf @@ -0,0 +1,22 @@ + + + + + + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + + + + Explicitely enable or disable tests parallelization + Explicitely enable or disable tests parallelization + + + + Enable tests parallelization + Enable tests parallelization + + + + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf new file mode 100644 index 0000000000..3f8f698191 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf @@ -0,0 +1,22 @@ + + + + + + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + By default, MSTest runs tests sequentially which can lead to sever performance limitation. It is recommended to enable assembly attribute 'Parallelize' or if the assembly is known to not be parallelizable, to use explicitly the assembly level attribute 'DoNotParallelize'. + + + + Explicitely enable or disable tests parallelization + Explicitely enable or disable tests parallelization + + + + Enable tests parallelization + Enable tests parallelization + + + + + \ No newline at end of file diff --git a/src/Package/MSTest.Sdk/Sdk/Sdk.props.template b/src/Package/MSTest.Sdk/Sdk/Sdk.props.template index 412fb5e4f1..661e0b865f 100644 --- a/src/Package/MSTest.Sdk/Sdk/Sdk.props.template +++ b/src/Package/MSTest.Sdk/Sdk/Sdk.props.template @@ -10,9 +10,9 @@ - - - - + + + + diff --git a/src/TestFramework/TestFramework/xlf/Resources.cs.xlf b/src/TestFramework/TestFramework/xlf/Resources.cs.xlf new file mode 100644 index 0000000000..773f29861a --- /dev/null +++ b/src/TestFramework/TestFramework/xlf/Resources.cs.xlf @@ -0,0 +1,22 @@ + + + + + + Type names should be all uppercase. + Type names should be all uppercase. + An optional longer localizable description of the diagnostic. + + + Type name '{0}' contains lowercase letters + Type name '{0}' contains lowercase letters + The format-able message the diagnostic displays. + + + Type name contains lowercase letters + Type name contains lowercase letters + The title of the diagnostic. + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework/xlf/Resources.de.xlf b/src/TestFramework/TestFramework/xlf/Resources.de.xlf new file mode 100644 index 0000000000..c096ec0b55 --- /dev/null +++ b/src/TestFramework/TestFramework/xlf/Resources.de.xlf @@ -0,0 +1,22 @@ + + + + + + Type names should be all uppercase. + Type names should be all uppercase. + An optional longer localizable description of the diagnostic. + + + Type name '{0}' contains lowercase letters + Type name '{0}' contains lowercase letters + The format-able message the diagnostic displays. + + + Type name contains lowercase letters + Type name contains lowercase letters + The title of the diagnostic. + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework/xlf/Resources.es.xlf b/src/TestFramework/TestFramework/xlf/Resources.es.xlf new file mode 100644 index 0000000000..593a87a996 --- /dev/null +++ b/src/TestFramework/TestFramework/xlf/Resources.es.xlf @@ -0,0 +1,22 @@ + + + + + + Type names should be all uppercase. + Type names should be all uppercase. + An optional longer localizable description of the diagnostic. + + + Type name '{0}' contains lowercase letters + Type name '{0}' contains lowercase letters + The format-able message the diagnostic displays. + + + Type name contains lowercase letters + Type name contains lowercase letters + The title of the diagnostic. + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework/xlf/Resources.fr.xlf b/src/TestFramework/TestFramework/xlf/Resources.fr.xlf new file mode 100644 index 0000000000..76bfb10e7f --- /dev/null +++ b/src/TestFramework/TestFramework/xlf/Resources.fr.xlf @@ -0,0 +1,22 @@ + + + + + + Type names should be all uppercase. + Type names should be all uppercase. + An optional longer localizable description of the diagnostic. + + + Type name '{0}' contains lowercase letters + Type name '{0}' contains lowercase letters + The format-able message the diagnostic displays. + + + Type name contains lowercase letters + Type name contains lowercase letters + The title of the diagnostic. + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework/xlf/Resources.it.xlf b/src/TestFramework/TestFramework/xlf/Resources.it.xlf new file mode 100644 index 0000000000..1c834a4b3a --- /dev/null +++ b/src/TestFramework/TestFramework/xlf/Resources.it.xlf @@ -0,0 +1,22 @@ + + + + + + Type names should be all uppercase. + Type names should be all uppercase. + An optional longer localizable description of the diagnostic. + + + Type name '{0}' contains lowercase letters + Type name '{0}' contains lowercase letters + The format-able message the diagnostic displays. + + + Type name contains lowercase letters + Type name contains lowercase letters + The title of the diagnostic. + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework/xlf/Resources.ja.xlf b/src/TestFramework/TestFramework/xlf/Resources.ja.xlf new file mode 100644 index 0000000000..926fb23991 --- /dev/null +++ b/src/TestFramework/TestFramework/xlf/Resources.ja.xlf @@ -0,0 +1,22 @@ + + + + + + Type names should be all uppercase. + Type names should be all uppercase. + An optional longer localizable description of the diagnostic. + + + Type name '{0}' contains lowercase letters + Type name '{0}' contains lowercase letters + The format-able message the diagnostic displays. + + + Type name contains lowercase letters + Type name contains lowercase letters + The title of the diagnostic. + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework/xlf/Resources.ko.xlf b/src/TestFramework/TestFramework/xlf/Resources.ko.xlf new file mode 100644 index 0000000000..0498579ebf --- /dev/null +++ b/src/TestFramework/TestFramework/xlf/Resources.ko.xlf @@ -0,0 +1,22 @@ + + + + + + Type names should be all uppercase. + Type names should be all uppercase. + An optional longer localizable description of the diagnostic. + + + Type name '{0}' contains lowercase letters + Type name '{0}' contains lowercase letters + The format-able message the diagnostic displays. + + + Type name contains lowercase letters + Type name contains lowercase letters + The title of the diagnostic. + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework/xlf/Resources.pl.xlf b/src/TestFramework/TestFramework/xlf/Resources.pl.xlf new file mode 100644 index 0000000000..1ed9425c8d --- /dev/null +++ b/src/TestFramework/TestFramework/xlf/Resources.pl.xlf @@ -0,0 +1,22 @@ + + + + + + Type names should be all uppercase. + Type names should be all uppercase. + An optional longer localizable description of the diagnostic. + + + Type name '{0}' contains lowercase letters + Type name '{0}' contains lowercase letters + The format-able message the diagnostic displays. + + + Type name contains lowercase letters + Type name contains lowercase letters + The title of the diagnostic. + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework/xlf/Resources.pt-BR.xlf b/src/TestFramework/TestFramework/xlf/Resources.pt-BR.xlf new file mode 100644 index 0000000000..f63129d392 --- /dev/null +++ b/src/TestFramework/TestFramework/xlf/Resources.pt-BR.xlf @@ -0,0 +1,22 @@ + + + + + + Type names should be all uppercase. + Type names should be all uppercase. + An optional longer localizable description of the diagnostic. + + + Type name '{0}' contains lowercase letters + Type name '{0}' contains lowercase letters + The format-able message the diagnostic displays. + + + Type name contains lowercase letters + Type name contains lowercase letters + The title of the diagnostic. + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework/xlf/Resources.ru.xlf b/src/TestFramework/TestFramework/xlf/Resources.ru.xlf new file mode 100644 index 0000000000..d6a8a99c2b --- /dev/null +++ b/src/TestFramework/TestFramework/xlf/Resources.ru.xlf @@ -0,0 +1,22 @@ + + + + + + Type names should be all uppercase. + Type names should be all uppercase. + An optional longer localizable description of the diagnostic. + + + Type name '{0}' contains lowercase letters + Type name '{0}' contains lowercase letters + The format-able message the diagnostic displays. + + + Type name contains lowercase letters + Type name contains lowercase letters + The title of the diagnostic. + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework/xlf/Resources.tr.xlf b/src/TestFramework/TestFramework/xlf/Resources.tr.xlf new file mode 100644 index 0000000000..7aafc310d1 --- /dev/null +++ b/src/TestFramework/TestFramework/xlf/Resources.tr.xlf @@ -0,0 +1,22 @@ + + + + + + Type names should be all uppercase. + Type names should be all uppercase. + An optional longer localizable description of the diagnostic. + + + Type name '{0}' contains lowercase letters + Type name '{0}' contains lowercase letters + The format-able message the diagnostic displays. + + + Type name contains lowercase letters + Type name contains lowercase letters + The title of the diagnostic. + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework/xlf/Resources.zh-Hans.xlf b/src/TestFramework/TestFramework/xlf/Resources.zh-Hans.xlf new file mode 100644 index 0000000000..c9ab34e68a --- /dev/null +++ b/src/TestFramework/TestFramework/xlf/Resources.zh-Hans.xlf @@ -0,0 +1,22 @@ + + + + + + Type names should be all uppercase. + Type names should be all uppercase. + An optional longer localizable description of the diagnostic. + + + Type name '{0}' contains lowercase letters + Type name '{0}' contains lowercase letters + The format-able message the diagnostic displays. + + + Type name contains lowercase letters + Type name contains lowercase letters + The title of the diagnostic. + + + + \ No newline at end of file diff --git a/src/TestFramework/TestFramework/xlf/Resources.zh-Hant.xlf b/src/TestFramework/TestFramework/xlf/Resources.zh-Hant.xlf new file mode 100644 index 0000000000..236a2e3f6e --- /dev/null +++ b/src/TestFramework/TestFramework/xlf/Resources.zh-Hant.xlf @@ -0,0 +1,22 @@ + + + + + + Type names should be all uppercase. + Type names should be all uppercase. + An optional longer localizable description of the diagnostic. + + + Type name '{0}' contains lowercase letters + Type name '{0}' contains lowercase letters + The format-able message the diagnostic displays. + + + Type name contains lowercase letters + Type name contains lowercase letters + The title of the diagnostic. + + + + \ No newline at end of file diff --git a/test/.editorconfig b/test/.editorconfig index 7fed58ea86..cbc5bb59ea 100644 --- a/test/.editorconfig +++ b/test/.editorconfig @@ -17,6 +17,9 @@ dotnet_diagnostic.SA1118.severity = none # SA1201: Elements should appear in the correct order dotnet_diagnostic.SA1201.severity = none +# CA1000: Do not declare static members on generic types +dotnet_diagnostic.CA1000.severity = suggestion + # CA1018: Mark attributes with AttributeUsageAttribute dotnet_diagnostic.CA1018.severity = none diff --git a/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs b/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs index 006df5b503..9aff180a97 100644 --- a/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs +++ b/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs @@ -104,7 +104,6 @@ private sealed class InternalFrameworkHandle : IFrameworkHandle private readonly List _messageList = []; private readonly ConcurrentDictionary> _testResults = new(); - private TestCase _activeTest; private ConcurrentBag _activeResults; public bool EnableShutdownAfterTestRun { get; set; } @@ -112,13 +111,11 @@ private sealed class InternalFrameworkHandle : IFrameworkHandle public void RecordStart(TestCase testCase) { _activeResults = _testResults.GetOrAdd(testCase, _ => new()); - _activeTest = testCase; } public void RecordEnd(TestCase testCase, TestOutcome outcome) { _activeResults = _testResults[testCase]; - _activeTest = testCase; } public void RecordResult(TestResult testResult) diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DeploymentTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DeploymentTests.cs index 4b96ad8b5c..f0a90b5911 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DeploymentTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/DeploymentTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Diagnostics.CodeAnalysis; + using Microsoft.MSTestV2.CLIAutomation; namespace MSTest.VstestConsoleWrapper.IntegrationTests; @@ -32,7 +34,7 @@ private void ValidateTestSourceDependencyDeployment(string targetFramework) public void ValidateTestSourceLocationDeployment_net462() => ValidateTestSourceLocationDeployment("net462"); - // TODO: Fix me + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "This is currently ignored and that's why we marked it as private")] private void ValidateTestSourceLocationDeployment_netcoreapp31() => ValidateTestSourceLocationDeployment("netcoreapp3.1"); diff --git a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataSourceTests.cs b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataSourceTests.cs index 6235d1e85c..71ca40fa64 100644 --- a/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataSourceTests.cs +++ b/test/IntegrationTests/MSTest.VstestConsoleWrapper.IntegrationTests/Parameterized tests/DataSourceTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Diagnostics.CodeAnalysis; + using Microsoft.MSTestV2.CLIAutomation; namespace MSTest.VstestConsoleWrapper.IntegrationTests; @@ -9,7 +11,8 @@ public class DataSourceTests : CLITestBase { private const string TestAssetName = "DataSourceTestProject"; - // TODO @haplois | @evangelink: This test fails under CI - will be fixed in a future PR (marked as private to ignore the test) + // TODO @haplois | @evangelink + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "This test fails under CI - will be fixed in a future PR (marked as private to ignore the test)")] private void ExecuteCsvTestDataSourceTests() { // Arrange & Act diff --git a/test/UnitTests/MSTest.Analyzers.Test/MSTest.Analyzers.Test.csproj b/test/UnitTests/MSTest.Analyzers.Test/MSTest.Analyzers.Test.csproj new file mode 100644 index 0000000000..85fe634f7e --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.Test/MSTest.Analyzers.Test.csproj @@ -0,0 +1,24 @@ + + + + net6.0 + + true + true + + + + + + + + + + + + + + + + + diff --git a/test/UnitTests/MSTest.Analyzers.Test/UseParallelizeAttributeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.Test/UseParallelizeAttributeAnalyzerTests.cs new file mode 100644 index 0000000000..2b6099d58f --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.Test/UseParallelizeAttributeAnalyzerTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Threading.Tasks; + +using TestFramework.ForTestingMSTest; + +using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.UseParallelizeAttributeAnalyzer, + MSTest.Analyzers.UseParallelizeAttributeFixer>; + +namespace MSTest.Analyzers.Test; + +public class UseParallelizeAttributeAnalyzerTests : TestContainer +{ + public async Task WhenNoAttributeSpecified_Diagnostic() + { + await VerifyCS.VerifyAnalyzerAsync( + string.Empty, + VerifyCS.Diagnostic(UseParallelizeAttributeAnalyzer.Rule).WithNoLocation()); + } + + public async Task WhenParallelizeAttributeSet_NoDiagnostic() + { + var code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [assembly: Parallelize(Workers = 2, Scope = ExecutionScope.MethodLevel)] + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenDoNotParallelizeAttributeSet_NoDiagnostic() + { + var code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [assembly: DoNotParallelize] + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.Test/Verifiers/CSharpCodeFixVerifier`2+Test.cs b/test/UnitTests/MSTest.Analyzers.Test/Verifiers/CSharpCodeFixVerifier`2+Test.cs new file mode 100644 index 0000000000..10708cab7d --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.Test/Verifiers/CSharpCodeFixVerifier`2+Test.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MSTest.Analyzers.Test; + +public static partial class CSharpCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() +{ + public class Test : CSharpCodeFixTest + { + public Test() + { + ReferenceAssemblies = ReferenceAssemblies.Net.Net60; + TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(ParallelizeAttribute).Assembly.Location)); + SolutionTransforms.Add((solution, projectId) => + { + var compilationOptions = solution.GetProject(projectId).CompilationOptions; + compilationOptions = compilationOptions.WithSpecificDiagnosticOptions( + compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings)); + solution = solution.WithProjectCompilationOptions(projectId, compilationOptions); + + return solution; + }); + } + } +} diff --git a/test/UnitTests/MSTest.Analyzers.Test/Verifiers/CSharpCodeFixVerifier`2.cs b/test/UnitTests/MSTest.Analyzers.Test/Verifiers/CSharpCodeFixVerifier`2.cs new file mode 100644 index 0000000000..17cd8228d7 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.Test/Verifiers/CSharpCodeFixVerifier`2.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Threading; +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 MSTest.Analyzers.Test; + +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(string source, params DiagnosticResult[] expected) + { + var test = new Test + { + TestCode = source, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + + /// + 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(CancellationToken.None); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.Test/Verifiers/CSharpVerifierHelper.cs b/test/UnitTests/MSTest.Analyzers.Test/Verifiers/CSharpVerifierHelper.cs new file mode 100644 index 0000000000..222cab6461 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.Test/Verifiers/CSharpVerifierHelper.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Immutable; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +namespace MSTest.Analyzers.Test; + +internal static class CSharpVerifierHelper +{ + /// + /// Gets 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); + var nullableWarnings = commandLineArguments.CompilationOptions.SpecificDiagnosticOptions; + + // Workaround for https://github.com/dotnet/roslyn/issues/41610 + nullableWarnings = nullableWarnings + .SetItem("CS8632", ReportDiagnostic.Error) + .SetItem("CS8669", ReportDiagnostic.Error); + + return nullableWarnings; + } +} diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDataSourceTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDataSourceTests.cs index 6be6f686bc..1b2f34f052 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDataSourceTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDataSourceTests.cs @@ -21,14 +21,10 @@ namespace MSTestAdapter.PlatformServices.UnitTests.Services; public class DesktopTestDataSourceTests : TestContainer { private readonly Mock _mockTestMethodInfo; - private readonly Mock _testMethod; - private readonly IDictionary _properties; private readonly Mock _mockTestContext; public DesktopTestDataSourceTests() { - _testMethod = new Mock(); - _properties = new Dictionary(); _mockTestMethodInfo = new Mock(); _mockTestContext = new Mock(); } diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs index 3e1d20f114..545c30c70b 100644 --- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs +++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs @@ -29,7 +29,9 @@ public class DesktopTestDeploymentTests : TestContainer private readonly Mock _mockReflectionUtility; private readonly Mock _mockFileUtility; +#pragma warning disable IDE0052 // Remove unread private members private IList _warnings; +#pragma warning restore IDE0052 // Remove unread private members public DesktopTestDeploymentTests() { @@ -118,7 +120,9 @@ public void DeployShouldCreateDeploymentDirectories() #region private methods +#pragma warning disable IDE0051 // Remove unused private members private void SetupDeploymentItems(MemberInfo memberInfo, KeyValuePair[] deploymentItems) +#pragma warning restore IDE0051 // Remove unused private members { var deploymentItemAttributes = new List(); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestExecutionManagerTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestExecutionManagerTests.cs index a2742e8cac..a83e6efb27 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestExecutionManagerTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestExecutionManagerTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; @@ -353,7 +354,7 @@ public void RunTestsShouldClearSessionParametersAcrossRuns() #region Run Tests on Sources - // TODO: This is currently ignored and that's why we marked it as private. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "This is currently ignored and that's why we marked it as private")] private void RunTestsForSourceShouldRunTestsInASource() { var sources = new List { Assembly.GetExecutingAssembly().Location }; @@ -365,7 +366,7 @@ private void RunTestsForSourceShouldRunTestsInASource() Verify(_frameworkHandle.ResultsList.Contains("PassingTest Passed")); } - // TODO: This is currently ignored and that's why we marked it as private. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "This is currently ignored and that's why we marked it as private")] private void RunTestsForSourceShouldPassInTestRunParametersInformationAsPropertiesToTheTest() { var sources = new List { Assembly.GetExecutingAssembly().Location }; @@ -385,7 +386,7 @@ private void RunTestsForSourceShouldPassInTestRunParametersInformationAsProperti new KeyValuePair("webAppUrl", "http://localhost"))); } - // TODO: This is currently ignored and that's why we marked it as private. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "This is currently ignored and that's why we marked it as private")] private void RunTestsForSourceShouldPassInDeploymentInformationAsPropertiesToTheTest() { var sources = new List { Assembly.GetExecutingAssembly().Location }; @@ -729,7 +730,7 @@ public void RunTestsForTestShouldPreferParallelSettingsFromRunSettingsOverAssemb } // This is tracked by https://github.com/Microsoft/testfx/issues/320. - // TODO: This is currently ignored and that's why we marked it as private. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "This is currently ignored and that's why we marked it as private")] private void RunTestsForTestShouldRunTestsInTheParentDomainsApartmentState() { var testCase1 = GetTestCase(typeof(DummyTestClassWithDoNotParallelizeMethods), "TestMethod1"); diff --git a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodRunnerTests.cs b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodRunnerTests.cs index 3b9bff8705..018ed88028 100644 --- a/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodRunnerTests.cs +++ b/test/UnitTests/MSTestAdapter.UnitTests/Execution/TestMethodRunnerTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; @@ -11,7 +12,6 @@ using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution; -using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations; @@ -28,8 +28,6 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Execution; public class TestMethodRunnerTests : TestContainer { - private readonly TestMethodRunner _globalTestMethodRunner; - private readonly MethodInfo _methodInfo; private readonly UTF.TestMethodAttribute _testMethodAttribute; @@ -40,12 +38,10 @@ public class TestMethodRunnerTests : TestContainer private readonly TestMethod _testMethod; - private readonly TestMethodOptions _globaltestMethodOptions; + private readonly TestMethodOptions _globalTestMethodOptions; private readonly TestMethodOptions _testMethodOptions; - private readonly Mock _mockReflectHelper; - private readonly TestablePlatformServiceProvider _testablePlatformServiceProvider; public TestMethodRunnerTests() @@ -66,7 +62,7 @@ public TestMethodRunnerTests() classAttribute: classAttribute, parent: testAssemblyInfo); - _globaltestMethodOptions = new TestMethodOptions() + _globalTestMethodOptions = new TestMethodOptions() { Timeout = 3600 * 1000, Executor = _testMethodAttribute, @@ -76,9 +72,8 @@ public TestMethodRunnerTests() var globalTestMethodInfo = new TestMethodInfo( _methodInfo, _testClassInfo, - _globaltestMethodOptions); + _globalTestMethodOptions); var testMethodInfo = new TestableTestmethodInfo(_methodInfo, _testClassInfo, _testMethodOptions, null); - _globalTestMethodRunner = new TestMethodRunner(globalTestMethodInfo, _testMethod, _testContextImplementation, false); _testMethodOptions = new TestMethodOptions() { @@ -88,8 +83,6 @@ public TestMethodRunnerTests() ExpectedException = null, }; - _mockReflectHelper = new Mock(); - // Reset test hooks DummyTestClass.TestConstructorMethodBody = () => { }; DummyTestClass.TestContextSetterBody = value => { }; @@ -475,6 +468,7 @@ public void RunTestMethodShouldSetResultFilesIfPresentForDataDrivenTests() #region Test data + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Use through reflection")] private static void InitMethodThrowingException(UTFExtension.TestContext tc) { // TODO: Fix exception type diff --git a/test/UnitTests/TestFramework.UnitTests/Attributes/DynamicDataAttributeTests.cs b/test/UnitTests/TestFramework.UnitTests/Attributes/DynamicDataAttributeTests.cs index 24df5ac374..a95d543ceb 100644 --- a/test/UnitTests/TestFramework.UnitTests/Attributes/DynamicDataAttributeTests.cs +++ b/test/UnitTests/TestFramework.UnitTests/Attributes/DynamicDataAttributeTests.cs @@ -476,7 +476,9 @@ public void DataRowTestMethod() /// /// The . /// +#pragma warning disable IDE0051 // Remove unused private members private static string GetDynamicDataDisplayNamePrivate(MethodInfo methodInfo, object[] data) +#pragma warning restore IDE0051 // Remove unused private members { throw new InvalidOperationException(); }