Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
Rule ID | Category | Severity | Notes
--------|----------|----------|-------
MSTEST0058 | Usage | Info | AvoidAssertsInCatchBlocksAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0058)
MSTEST0059 | Usage | Warning | DoNotUseParallelizeAndDoNotParallelizeTogetherAnalyzer
1 change: 1 addition & 0 deletions src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,5 @@ internal static class DiagnosticIds
public const string TestMethodAttributeShouldSetDisplayNameCorrectlyRuleId = "MSTEST0056";
public const string TestMethodAttributeShouldPropagateSourceInformationRuleId = "MSTEST0057";
public const string AvoidAssertsInCatchBlocksRuleId = "MSTEST0058";
public const string DoNotUseParallelizeAndDoNotParallelizeTogetherRuleId = "MSTEST0059";
}
12 changes: 12 additions & 0 deletions src/Analyzers/MSTest.Analyzers/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -693,4 +693,16 @@ The type declaring these methods should also respect the following rules:
<data name="AvoidAssertsInCatchBlocksDescription" xml:space="preserve">
<value>Using asserts in catch blocks is problematic because the test will pass even if no exception is thrown and the catch block is never executed. Use 'Assert.Throws', 'Assert.ThrowsExactly', 'Assert.ThrowsAsync' or 'Assert.ThrowsExactlyAsync' to verify that an exception is thrown, and then make additional assertions on the caught exception without using the try-catch block.</value>
</data>
<data name="DoNotUseParallelizeAndDoNotParallelizeTogetherTitle" xml:space="preserve">
<value>Do not use both '[Parallelize]' and '[DoNotParallelize]' attributes</value>
<comment>{Locked="[Parallelize]"}{Locked="[DoNotParallelize]"}</comment>
</data>
<data name="DoNotUseParallelizeAndDoNotParallelizeTogetherMessageFormat" xml:space="preserve">
<value>Assembly has both '[Parallelize]' and '[DoNotParallelize]' attributes which creates ambiguity</value>
<comment>{Locked="[Parallelize]"}{Locked="[DoNotParallelize]"}</comment>
</data>
<data name="DoNotUseParallelizeAndDoNotParallelizeTogetherDescription" xml:space="preserve">
<value>An assembly should have either '[Parallelize]' or '[DoNotParallelize]' attribute, but not both. Having both attributes creates an ambiguous configuration. When both are present, '[DoNotParallelize]' takes precedence and parallelization will be disabled.</value>
<comment>{Locked="[Parallelize]"}{Locked="[DoNotParallelize]"}</comment>
</data>
</root>
64 changes: 48 additions & 16 deletions src/Analyzers/MSTest.Analyzers/UseParallelizeAttributeAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace MSTest.Analyzers;

/// <summary>
/// MSTEST0001: <inheritdoc cref="Resources.UseParallelizeAttributeAnalyzerTitle"/>.
/// MSTEST0058: <inheritdoc cref="Resources.DoNotUseParallelizeAndDoNotParallelizeTogetherTitle"/>.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public sealed class UseParallelizeAttributeAnalyzer : DiagnosticAnalyzer
Expand All @@ -22,6 +23,10 @@ public sealed class UseParallelizeAttributeAnalyzer : DiagnosticAnalyzer
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));

private static readonly LocalizableResourceString BothAttributesTitle = new(nameof(Resources.DoNotUseParallelizeAndDoNotParallelizeTogetherTitle), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableResourceString BothAttributesMessageFormat = new(nameof(Resources.DoNotUseParallelizeAndDoNotParallelizeTogetherMessageFormat), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableResourceString BothAttributesDescription = new(nameof(Resources.DoNotUseParallelizeAndDoNotParallelizeTogetherDescription), Resources.ResourceManager, typeof(Resources));

/// <inheritdoc cref="Resources.UseParallelizeAttributeAnalyzerTitle" />
public static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(
DiagnosticIds.UseParallelizedAttributeRuleId,
Expand All @@ -32,9 +37,19 @@ public sealed class UseParallelizeAttributeAnalyzer : DiagnosticAnalyzer
DiagnosticSeverity.Warning,
isEnabledByDefault: true);

/// <inheritdoc cref="Resources.DoNotUseParallelizeAndDoNotParallelizeTogetherTitle" />
public static readonly DiagnosticDescriptor DoNotUseBothAttributesRule = DiagnosticDescriptorHelper.Create(
DiagnosticIds.DoNotUseParallelizeAndDoNotParallelizeTogetherRuleId,
BothAttributesTitle,
BothAttributesMessageFormat,
BothAttributesDescription,
Category.Usage,
DiagnosticSeverity.Warning,
isEnabledByDefault: true);

/// <inheritdoc />
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
= ImmutableArray.Create(Rule);
= ImmutableArray.Create(Rule, DoNotUseBothAttributesRule);

/// <inheritdoc />
public override void Initialize(AnalysisContext context)
Expand All @@ -47,35 +62,52 @@ public override void Initialize(AnalysisContext context)

private static void AnalyzeCompilation(CompilationAnalysisContext context)
{
bool hasTestAdapter = context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.IsMSTestTestAdapterReferenced", out string? isAdapterReferenced) &&
bool.TryParse(isAdapterReferenced, out bool isAdapterReferencedValue) &&
isAdapterReferencedValue;

if (!hasTestAdapter)
{
// We shouldn't produce a diagnostic if only the test framework is referenced, but not the adapter.
return;
}

INamedTypeSymbol? parallelizeAttributeSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingParallelizeAttribute);
INamedTypeSymbol? doNotParallelizeAttributeSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingDoNotParallelizeAttribute);

bool hasParallelizeAttribute = false;
bool hasDoNotParallelizeAttribute = false;
AttributeData? parallelizeAttribute = null;
AttributeData? doNotParallelizeAttribute = null;
foreach (AttributeData attribute in context.Compilation.Assembly.GetAttributes())
{
if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, parallelizeAttributeSymbol))
{
hasParallelizeAttribute = true;
parallelizeAttribute = attribute;
}

if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, doNotParallelizeAttributeSymbol))
{
hasDoNotParallelizeAttribute = true;
doNotParallelizeAttribute = attribute;
}
}

if (parallelizeAttribute is not null && doNotParallelizeAttribute is not null)
{
// Both attributes are present - this is an error
// Report on both attribute locations
if (parallelizeAttribute.ApplicationSyntaxReference is not null)
{
context.ReportDiagnostic(parallelizeAttribute.ApplicationSyntaxReference.CreateDiagnostic(DoNotUseBothAttributesRule, context.CancellationToken));
}

if (doNotParallelizeAttribute.ApplicationSyntaxReference is not null)
{
context.ReportDiagnostic(doNotParallelizeAttribute.ApplicationSyntaxReference.CreateDiagnostic(DoNotUseBothAttributesRule, context.CancellationToken));
}

return;
}

bool hasTestAdapter = context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.IsMSTestTestAdapterReferenced", out string? isAdapterReferenced) &&
bool.TryParse(isAdapterReferenced, out bool isAdapterReferencedValue) &&
isAdapterReferencedValue;

if (!hasTestAdapter)
{
// We shouldn't produce a diagnostic if only the test framework is referenced, but not the adapter.
return;
}

if (!hasParallelizeAttribute && !hasDoNotParallelizeAttribute)
if (parallelizeAttribute is null && doNotParallelizeAttribute is null)
{
// We cannot provide any good location for assembly level missing attributes
context.ReportNoLocationDiagnostic(Rule);
Expand Down
43 changes: 29 additions & 14 deletions src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="cs" original="../Resources.resx">
<body>
Expand All @@ -19,7 +19,7 @@ The type declaring these methods should also respect the following rules:
-The class shouldn't be 'static'
-The class should be marked with '[TestClass]' (or a derived attribute)
-the class should not be generic.</source>
<target state="translated">Aby byly metody s označením [AssemblyCleanup] platné, musí se řídit následujícím rozložením:
<target state="translated">Aby byly metody s označením [AssemblyCleanup] platné, musí se řídit následujícím rozložením:
– Nesmí být deklarované pro obecnou třídu.
– Musí být public.
– Musí být static.
Expand All @@ -29,7 +29,7 @@ The type declaring these methods should also respect the following rules:
– Nesmí přijímat žádný parametr, nebo musí přijímat jediný parametr typu TestContext.
– Návratový typ musí být void, Task nebo ValueTask.

Typ deklarující tyto metody by měl také respektovat následující pravidla:
Typ deklarující tyto metody by měl také respektovat následující pravidla:
– Typ by měl být třída.
– Třída by měla být public nebo internal (pokud testovací projekt používá atribut [DiscoverInternals]).
– Třída by neměla být static.
Expand Down Expand Up @@ -64,7 +64,7 @@ The type declaring these methods should also respect the following rules:
-The class shouldn't be 'static'
-The class should be marked with '[TestClass]' (or a derived attribute)
-the class should not be generic.</source>
<target state="translated">Aby byly metody s označením [AssemblyInitialize] platné, musí se řídit následujícím rozložením:
<target state="translated">Aby byly metody s označením [AssemblyInitialize] platné, musí se řídit následujícím rozložením:
– Nesmí být deklarované pro obecnou třídu.
– Musí být public.
– Musí být static.
Expand All @@ -74,7 +74,7 @@ The type declaring these methods should also respect the following rules:
– Musí mít jeden parametr typu TestContext.
– Návratový typ musí být void, Task nebo ValueTask.

Typ deklarující tyto metody by měl také respektovat následující pravidla:
Typ deklarující tyto metody by měl také respektovat následující pravidla:
– Typ by měl být třída.
– Třída by měla být public nebo internal (pokud testovací projekt používá atribut [DiscoverInternals]).
– Třída by neměla být static.
Expand Down Expand Up @@ -176,7 +176,7 @@ The type declaring these methods should also respect the following rules:
-The class shouldn't be 'static'
-If the class is 'sealed', it should be marked with '[TestClass]' (or a derived attribute)
-the class should not be generic.</source>
<target state="translated">Aby byly metody s označením [ClassCleanup] platné, musí se řídit následujícím rozložením:
<target state="translated">Aby byly metody s označením [ClassCleanup] platné, musí se řídit následujícím rozložením:
– Nesmí být deklarované pro obecnou třídu bez nastavení režimu InheritanceBehavior.
– Musí být public.
– Musí být static.
Expand All @@ -188,7 +188,7 @@ The type declaring these methods should also respect the following rules:
– V případě třídy abstract by měl být zadán parametr atributu InheritanceBehavior.BeforeEachDerivedClass.
– V případě třídy sealed by neměl být zadán parametr atributu InheritanceBehavior.BeforeEachDerivedClass.

Typ deklarující tyto metody by měl také respektovat následující pravidla:
Typ deklarující tyto metody by měl také respektovat následující pravidla:
– Typ by měl být třída.
– Třída by měla být public nebo internal (pokud testovací projekt používá atribut [DiscoverInternals]).
– Třída by neměla být static.
Expand Down Expand Up @@ -225,7 +225,7 @@ The type declaring these methods should also respect the following rules:
-The class shouldn't be 'static'
-If the class is 'sealed', it should be marked with '[TestClass]' (or a derived attribute)
-the class should not be generic.</source>
<target state="translated">Aby byly metody s označením [ClassInitialize] platné, musí se řídit následujícím rozložením:
<target state="translated">Aby byly metody s označením [ClassInitialize] platné, musí se řídit následujícím rozložením:
– Nesmí být deklarované pro obecnou třídu bez nastavení režimu InheritanceBehavior.
– Musí být public.
– Musí být static.
Expand All @@ -237,7 +237,7 @@ The type declaring these methods should also respect the following rules:
– V případě třídy abstract by měl být zadán parametr atributu InheritanceBehavior.BeforeEachDerivedClass.
– V případě třídy sealed by neměl být zadán parametr atributu InheritanceBehavior.BeforeEachDerivedClass.

Typ deklarující tyto metody by měl také respektovat následující pravidla:
Typ deklarující tyto metody by měl také respektovat následující pravidla:
– Typ by měl být třída.
– Třída by měla být public nebo internal (pokud testovací projekt používá atribut [DiscoverInternals]).
– Třída by neměla být static.
Expand Down Expand Up @@ -479,7 +479,7 @@ The type declaring these methods should also respect the following rules:
– Musí mít jeden parametr typu TestContext.
– Návratový typ musí být „void“, „Task“ nebo „ValueTask“.

Typ deklarující tyto metody by měl také respektovat následující pravidla:
Typ deklarující tyto metody by měl také respektovat následující pravidla:
– Typ by měl být třída.
– Třída by měla být „public“.
– Třída by neměla být „static“.
Expand Down Expand Up @@ -724,7 +724,7 @@ The type declaring these methods should also respect the following rules:
-The class should be 'public' or 'internal' (if the test project is using the '[DiscoverInternals]' attribute)
-The class shouldn't be 'static'
-If the class is 'sealed', it should be marked with '[TestClass]' (or a derived attribute).</source>
<target state="translated">Aby byly metody s označením [TestCleanup] platné, musí se řídit následujícím rozložením:
<target state="translated">Aby byly metody s označením [TestCleanup] platné, musí se řídit následujícím rozložením:
– Musí být public.
– Nesmí být abstract.
– Nesmí být async void.
Expand All @@ -734,7 +734,7 @@ The type declaring these methods should also respect the following rules:
– Nesmí přijímat žádný parametr.
– Návratový typ musí být void, Task nebo ValueTask.

Typ deklarující tyto metody by měl také respektovat následující pravidla:
Typ deklarující tyto metody by měl také respektovat následující pravidla:
– Typ by měl být třída.
– Třída by měla být public nebo internal (pokud testovací projekt používá atribut [DiscoverInternals]).
– Třída by neměla být static.
Expand Down Expand Up @@ -783,7 +783,7 @@ The type declaring these methods should also respect the following rules:
-The class should be 'public' or 'internal' (if the test project is using the '[DiscoverInternals]' attribute)
-The class shouldn't be 'static'
-If the class is 'sealed', it should be marked with '[TestClass]' (or a derived attribute).</source>
<target state="translated">Aby byly metody s označením [TestInitialize] platné, musí se řídit následujícím rozložením:
<target state="translated">Aby byly metody s označením [TestInitialize] platné, musí se řídit následujícím rozložením:
– Musí být public.
– Nesmí být abstract.
– Nesmí být async void.
Expand All @@ -793,7 +793,7 @@ The type declaring these methods should also respect the following rules:
– Nesmí přijímat žádný parametr.
– Návratový typ musí být void, Task nebo ValueTask.

Typ deklarující tyto metody by měl také respektovat následující pravidla:
Typ deklarující tyto metody by měl také respektovat následující pravidla:
– Typ by měl být třída.
– Třída by měla být public nebo internal (pokud testovací projekt používá atribut [DiscoverInternals]).
– Třída by neměla být static.
Expand Down Expand Up @@ -1004,6 +1004,21 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla:
<target state="translated">Používání kontrolních výrazů v blocích catch je problematické, protože test projde, i když se nevyvolá žádná výjimka a blok catch se nikdy nespustí. K ověření, že je vyvolána výjimka, použijte metody Assert.Throws, Assert.ThrowsExactly, Assert.ThrowsAsync nebo Assert.ThrowsExactlyAsync a poté proveďte další kontrolní výrazy nad zachycenou výjimkou bez použití bloku try-catch.</target>
<note />
</trans-unit>
<trans-unit id="DoNotUseParallelizeAndDoNotParallelizeTogetherDescription">
<source>An assembly should have either '[Parallelize]' or '[DoNotParallelize]' attribute, but not both. Having both attributes creates an ambiguous configuration. When both are present, '[DoNotParallelize]' takes precedence and parallelization will be disabled.</source>
<target state="new">An assembly should have either '[Parallelize]' or '[DoNotParallelize]' attribute, but not both. Having both attributes creates an ambiguous configuration. When both are present, '[DoNotParallelize]' takes precedence and parallelization will be disabled.</target>
<note>{Locked="[Parallelize]"}{Locked="[DoNotParallelize]"}</note>
</trans-unit>
<trans-unit id="DoNotUseParallelizeAndDoNotParallelizeTogetherMessageFormat">
<source>Assembly has both '[Parallelize]' and '[DoNotParallelize]' attributes which creates ambiguity</source>
<target state="new">Assembly has both '[Parallelize]' and '[DoNotParallelize]' attributes which creates ambiguity</target>
<note>{Locked="[Parallelize]"}{Locked="[DoNotParallelize]"}</note>
</trans-unit>
<trans-unit id="DoNotUseParallelizeAndDoNotParallelizeTogetherTitle">
<source>Do not use both '[Parallelize]' and '[DoNotParallelize]' attributes</source>
<target state="new">Do not use both '[Parallelize]' and '[DoNotParallelize]' attributes</target>
<note>{Locked="[Parallelize]"}{Locked="[DoNotParallelize]"}</note>
</trans-unit>
</body>
</file>
</xliff>
Loading
Loading