diff --git a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md
index 43b4b1fb3b..60a1fce5b5 100644
--- a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md
+++ b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md
@@ -10,3 +10,4 @@ MSTEST0065 | Usage | Warning | AvoidAssertAreEqualOnCollectionsAnalyzer, [Docume
MSTEST0066 | Design | Info | IgnoreShouldHaveJustificationAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0066)
MSTEST0067 | Usage | Disabled | AvoidThreadSleepAndTaskWaitInTestsAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0067)
MSTEST0068 | Usage | Info | CollectionAssertToAssertAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0068)
+MSTEST0070 | Usage | Warning | MemberConditionShouldBeValidAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0070)
diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs
index 6774a4ef0b..585c7b2398 100644
--- a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs
+++ b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// 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;
@@ -74,4 +74,5 @@ internal static class DiagnosticIds
public const string AvoidThreadSleepAndTaskWaitInTestsRuleId = "MSTEST0067";
public const string CollectionAssertToAssertRuleId = "MSTEST0068";
// public const string InheritedTestClassAttributeWithSourceGeneratorRuleId = "MSTEST0069"; - // Reserved. Owned by MSTest.SourceGeneration analyzer; don't reuse this ID.
+ public const string MemberConditionShouldBeValidRuleId = "MSTEST0070";
}
diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs
index 69a0d144e2..1336545d01 100644
--- a/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs
+++ b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// 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;
@@ -29,6 +29,8 @@ internal static class WellKnownTypeNames
public const string MicrosoftVisualStudioTestToolsUnitTestingGlobalTestInitializeAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.GlobalTestInitializeAttribute";
public const string MicrosoftVisualStudioTestToolsUnitTestingIgnoreAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.IgnoreAttribute";
public const string MicrosoftVisualStudioTestToolsUnitTestingInheritanceBehavior = "Microsoft.VisualStudio.TestTools.UnitTesting.InheritanceBehavior";
+ public const string MicrosoftVisualStudioTestToolsUnitTestingMemberConditionAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.MemberConditionAttribute";
+ public const string MicrosoftVisualStudioTestToolsUnitTestingOSConditionAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.OSConditionAttribute";
public const string MicrosoftVisualStudioTestToolsUnitTestingOwnerAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.OwnerAttribute";
public const string MicrosoftVisualStudioTestToolsUnitTestingParallelizeAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.ParallelizeAttribute";
public const string MicrosoftVisualStudioTestToolsUnitTestingPriorityAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.PriorityAttribute";
@@ -42,7 +44,6 @@ internal static class WellKnownTypeNames
public const string MicrosoftVisualStudioTestToolsUnitTestingTestPropertyAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute";
public const string MicrosoftVisualStudioTestToolsUnitTestingTimeoutAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TimeoutAttribute";
public const string MicrosoftVisualStudioTestToolsUnitTestingWorkItemAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.WorkItemAttribute";
- public const string MicrosoftVisualStudioTestToolsUnitTestingOSConditionAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.OSConditionAttribute";
public const string System = "System";
public const string SystemRuntimeInteropServicesRuntimeInformation = "System.Runtime.InteropServices.RuntimeInformation";
diff --git a/src/Analyzers/MSTest.Analyzers/MemberConditionShouldBeValidAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/MemberConditionShouldBeValidAnalyzer.cs
new file mode 100644
index 0000000000..297a9ae60f
--- /dev/null
+++ b/src/Analyzers/MSTest.Analyzers/MemberConditionShouldBeValidAnalyzer.cs
@@ -0,0 +1,344 @@
+// 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 Analyzer.Utilities.Extensions;
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+using MSTest.Analyzers.Helpers;
+
+namespace MSTest.Analyzers;
+
+///
+/// MSTEST0070: .
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
+public sealed class MemberConditionShouldBeValidAnalyzer : DiagnosticAnalyzer
+{
+ private static readonly LocalizableResourceString Title = new(nameof(Resources.MemberConditionShouldBeValidTitle), Resources.ResourceManager, typeof(Resources));
+ private static readonly LocalizableResourceString Description = new(nameof(Resources.MemberConditionShouldBeValidDescription), Resources.ResourceManager, typeof(Resources));
+
+ ///
+ public static readonly DiagnosticDescriptor MemberNotFoundRule = DiagnosticDescriptorHelper.Create(
+ DiagnosticIds.MemberConditionShouldBeValidRuleId,
+ Title,
+ new LocalizableResourceString(nameof(Resources.MemberConditionShouldBeValidMessageFormat_MemberNotFound), Resources.ResourceManager, typeof(Resources)),
+ Description,
+ Category.Usage,
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
+ ///
+ public static readonly DiagnosticDescriptor MemberNotPublicRule = MemberNotFoundRule
+ .WithMessage(new(nameof(Resources.MemberConditionShouldBeValidMessageFormat_MemberNotPublic), Resources.ResourceManager, typeof(Resources)));
+
+ ///
+ public static readonly DiagnosticDescriptor MemberNotStaticRule = MemberNotFoundRule
+ .WithMessage(new(nameof(Resources.MemberConditionShouldBeValidMessageFormat_MemberNotStatic), Resources.ResourceManager, typeof(Resources)));
+
+ ///
+ public static readonly DiagnosticDescriptor MemberWrongKindRule = MemberNotFoundRule
+ .WithMessage(new(nameof(Resources.MemberConditionShouldBeValidMessageFormat_MemberWrongKind), Resources.ResourceManager, typeof(Resources)));
+
+ ///
+ public static readonly DiagnosticDescriptor MemberWrongReturnTypeRule = MemberNotFoundRule
+ .WithMessage(new(nameof(Resources.MemberConditionShouldBeValidMessageFormat_MemberWrongReturnType), Resources.ResourceManager, typeof(Resources)));
+
+ ///
+ public static readonly DiagnosticDescriptor MethodHasParametersRule = MemberNotFoundRule
+ .WithMessage(new(nameof(Resources.MemberConditionShouldBeValidMessageFormat_MethodHasParameters), Resources.ResourceManager, typeof(Resources)));
+
+ ///
+ public static readonly DiagnosticDescriptor PropertyNotReadableRule = MemberNotFoundRule
+ .WithMessage(new(nameof(Resources.MemberConditionShouldBeValidMessageFormat_PropertyNotReadable), Resources.ResourceManager, typeof(Resources)));
+
+ ///
+ public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(
+ MemberNotFoundRule,
+ MemberNotPublicRule,
+ MemberNotStaticRule,
+ MemberWrongKindRule,
+ MemberWrongReturnTypeRule,
+ MethodHasParametersRule,
+ PropertyNotReadableRule);
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+
+ context.RegisterCompilationStartAction(context =>
+ {
+ if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingMemberConditionAttribute, out INamedTypeSymbol? conditionAttributeSymbol))
+ {
+ return;
+ }
+
+ context.RegisterSymbolAction(
+ ctx => AnalyzeSymbol(ctx, conditionAttributeSymbol),
+ SymbolKind.Method,
+ SymbolKind.NamedType);
+ });
+ }
+
+ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol conditionAttributeSymbol)
+ {
+ foreach (AttributeData attribute in context.Symbol.GetAttributes())
+ {
+ if (!SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, conditionAttributeSymbol))
+ {
+ continue;
+ }
+
+ AnalyzeAttribute(context, attribute);
+ }
+ }
+
+ private static void AnalyzeAttribute(SymbolAnalysisContext context, AttributeData attribute)
+ {
+ if (attribute.ApplicationSyntaxReference?.GetSyntax(context.CancellationToken) is not { } attributeSyntax)
+ {
+ return;
+ }
+
+ // Walk the constructor arguments. Across the 4 ctor overloads
+ // ( (Type, string), (Type, string, params string[]),
+ // (ConditionMode, Type, string), (ConditionMode, Type, string, params string[]) )
+ // we can identify the condition type, the first member name, and the optional params array
+ // by inspecting argument kinds and types.
+ ITypeSymbol? conditionType = null;
+ var memberNames = new List();
+ foreach (TypedConstant argument in attribute.ConstructorArguments)
+ {
+ if (argument.IsNull)
+ {
+ continue;
+ }
+
+ if (argument.Kind == TypedConstantKind.Type && argument.Value is ITypeSymbol typeValue)
+ {
+ conditionType = typeValue;
+ }
+ else if (argument.Kind == TypedConstantKind.Primitive && argument.Value is string singleName)
+ {
+ memberNames.Add(singleName);
+ }
+ else if (argument.Kind == TypedConstantKind.Array)
+ {
+ foreach (TypedConstant element in argument.Values.Where(static e => !e.IsNull && e.Value is string))
+ {
+ memberNames.Add((string)element.Value!);
+ }
+ }
+ }
+
+ if (conditionType is null || memberNames.Count == 0)
+ {
+ return;
+ }
+
+ string typeName = conditionType.Name;
+
+ // Non-named types (arrays, pointers, function pointers) can't carry user-declared static
+ // bool members the way [MemberCondition] requires. The runtime will throw
+ // ``InvalidOperationException`` at first ``IsConditionMet`` access; surface that as
+ // MSTEST0070 (MemberNotFound) here so the user sees it at edit-time.
+ if (conditionType is not INamedTypeSymbol namedConditionType)
+ {
+ string nonNamedTypeName = string.IsNullOrEmpty(conditionType.Name)
+ ? conditionType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)
+ : conditionType.Name;
+ foreach (string memberName in memberNames)
+ {
+ if (!string.IsNullOrWhiteSpace(memberName))
+ {
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberNotFoundRule, nonNamedTypeName, memberName));
+ }
+ }
+
+ return;
+ }
+
+ foreach (string memberName in memberNames)
+ {
+ ValidateMember(context, attributeSyntax, namedConditionType, typeName, memberName);
+ }
+ }
+
+ private static void ValidateMember(SymbolAnalysisContext context, SyntaxNode attributeSyntax, INamedTypeSymbol conditionType, string typeName, string memberName)
+ {
+ if (string.IsNullOrWhiteSpace(memberName))
+ {
+ // The runtime constructor already throws ArgumentException for null/empty/whitespace
+ // names. Nothing useful to validate here.
+ return;
+ }
+
+ ImmutableArray candidates = LookupMember(conditionType, memberName);
+ if (candidates.IsEmpty)
+ {
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberNotFoundRule, typeName, memberName));
+ return;
+ }
+
+ // Match the runtime resolution order: GetProperty / GetField / GetMethod with
+ // BindingFlags.Public | Static | FlattenHierarchy, where GetMethod looks for the
+ // *parameterless* overload only. If the runtime would bind to a candidate that
+ // satisfies those filters, prefer it so we don't report a false positive against an
+ // instance member or a parameterized overload that shadows the real binding target.
+ ISymbol? selected =
+ // Property: public + static + non-indexer (runtime rejects indexers).
+ candidates.OfType()
+ .FirstOrDefault(static p => p.DeclaredAccessibility == Accessibility.Public && p.IsStatic && !p.IsIndexer)
+ // Field: public + static.
+ ?? (ISymbol?)candidates.OfType()
+ .FirstOrDefault(static f => f.DeclaredAccessibility == Accessibility.Public && f.IsStatic)
+ // Method: public + static + ordinary + parameterless.
+ ?? candidates.OfType()
+ .FirstOrDefault(static m =>
+ m.DeclaredAccessibility == Accessibility.Public
+ && m.IsStatic
+ && m.MethodKind == MethodKind.Ordinary
+ && m.Parameters.Length == 0)
+ // Fallback when no runtime-binding candidate exists: pick the first member by
+ // kind so the more specific diagnostic (not-public, not-static, etc.) is reported.
+ ?? candidates.FirstOrDefault(static s => s.Kind == SymbolKind.Property)
+ ?? candidates.FirstOrDefault(static s => s.Kind == SymbolKind.Field)
+ ?? candidates.FirstOrDefault(static s => s.Kind == SymbolKind.Method);
+
+ if (selected is null)
+ {
+ // A nested type, event, or other unsupported member kind is shadowing the name.
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberWrongKindRule, typeName, memberName));
+ return;
+ }
+
+ switch (selected)
+ {
+ case IPropertySymbol property:
+ ValidateProperty(context, attributeSyntax, typeName, memberName, property);
+ break;
+
+ case IFieldSymbol field:
+ ValidateField(context, attributeSyntax, typeName, memberName, field);
+ break;
+
+ case IMethodSymbol method:
+ ValidateMethod(context, attributeSyntax, typeName, memberName, method);
+ break;
+
+ default:
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberWrongKindRule, typeName, memberName));
+ break;
+ }
+ }
+
+ private static ImmutableArray LookupMember(INamedTypeSymbol type, string memberName)
+ {
+ // Walk the type hierarchy so inherited public static members are also recognized,
+ // matching what reflection with `BindingFlags.Public | Static | FlattenHierarchy` would find.
+ ImmutableArray.Builder builder = ImmutableArray.CreateBuilder();
+ INamedTypeSymbol? current = type;
+ while (current is not null)
+ {
+ foreach (ISymbol member in current.GetMembers(memberName))
+ {
+ builder.Add(member);
+ }
+
+ current = current.BaseType;
+ }
+
+ return builder.ToImmutable();
+ }
+
+ private static void ValidateProperty(SymbolAnalysisContext context, SyntaxNode attributeSyntax, string typeName, string memberName, IPropertySymbol property)
+ {
+ if (property.IsIndexer)
+ {
+ // Indexer properties (e.g. public static bool this[int i] in C# 13+) are rejected by
+ // the runtime because the attribute requires a *parameterless* readable property.
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberWrongKindRule, typeName, memberName));
+ return;
+ }
+
+ if (property.DeclaredAccessibility != Accessibility.Public)
+ {
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberNotPublicRule, typeName, memberName));
+ return;
+ }
+
+ if (!property.IsStatic)
+ {
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberNotStaticRule, typeName, memberName));
+ return;
+ }
+
+ if (property.GetMethod is null || property.GetMethod.DeclaredAccessibility != Accessibility.Public)
+ {
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(PropertyNotReadableRule, typeName, memberName));
+ return;
+ }
+
+ if (property.Type.SpecialType != SpecialType.System_Boolean)
+ {
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberWrongReturnTypeRule, typeName, memberName));
+ }
+ }
+
+ private static void ValidateField(SymbolAnalysisContext context, SyntaxNode attributeSyntax, string typeName, string memberName, IFieldSymbol field)
+ {
+ if (field.DeclaredAccessibility != Accessibility.Public)
+ {
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberNotPublicRule, typeName, memberName));
+ return;
+ }
+
+ if (!field.IsStatic)
+ {
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberNotStaticRule, typeName, memberName));
+ return;
+ }
+
+ if (field.Type.SpecialType != SpecialType.System_Boolean)
+ {
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberWrongReturnTypeRule, typeName, memberName));
+ }
+ }
+
+ private static void ValidateMethod(SymbolAnalysisContext context, SyntaxNode attributeSyntax, string typeName, string memberName, IMethodSymbol method)
+ {
+ if (method.MethodKind != MethodKind.Ordinary)
+ {
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberWrongKindRule, typeName, memberName));
+ return;
+ }
+
+ if (method.DeclaredAccessibility != Accessibility.Public)
+ {
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberNotPublicRule, typeName, memberName));
+ return;
+ }
+
+ if (!method.IsStatic)
+ {
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberNotStaticRule, typeName, memberName));
+ return;
+ }
+
+ if (method.Parameters.Length > 0)
+ {
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MethodHasParametersRule, typeName, memberName));
+ return;
+ }
+
+ if (method.ReturnType.SpecialType != SpecialType.System_Boolean)
+ {
+ context.ReportDiagnostic(attributeSyntax.CreateDiagnostic(MemberWrongReturnTypeRule, typeName, memberName));
+ }
+ }
+}
diff --git a/src/Analyzers/MSTest.Analyzers/Resources.resx b/src/Analyzers/MSTest.Analyzers/Resources.resx
index 0db7f0d359..af9597b960 100644
--- a/src/Analyzers/MSTest.Analyzers/Resources.resx
+++ b/src/Analyzers/MSTest.Analyzers/Resources.resx
@@ -950,4 +950,40 @@ The type declaring these methods should also respect the following rules:
An '[Ignore]' attribute applied to a test method or test class should include a non-empty message explaining why the test or class is ignored. A justification message makes it easier to triage skipped tests, helps reviewers understand the intent, and prevents tests from being silently disabled forever.{Locked="[Ignore]"}{Locked="class"}
+
+ '[MemberCondition]' arguments should be valid
+ {Locked="[MemberCondition]"}
+
+
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ {Locked="[MemberCondition]"}{Locked="public static"}{Locked="bool"}
+
+
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="public"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="static"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="bool"}
+
+
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ {0} is the containing type name. {1} is the method name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ {0} is the containing type name. {1} is the property name. {Locked="[MemberCondition]"}
+
diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf
index b2f8fb7724..c3132e9b4b 100644
--- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf
+++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf
@@ -1,4 +1,4 @@
-
+
@@ -295,6 +295,51 @@ The type declaring these methods should also respect the following rules:
Použijte Assert namísto CollectionAssert{Locked="CollectionAssert"}{Locked="Assert"}
+
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ {Locked="[MemberCondition]"}{Locked="public static"}{Locked="bool"}
+
+
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="public"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="static"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="bool"}
+
+
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ {0} is the containing type name. {1} is the method name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ {0} is the containing type name. {1} is the property name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' arguments should be valid
+ '[MemberCondition]' arguments should be valid
+ {Locked="[MemberCondition]"}
+ DataRow entry should have the following layout to be valid:
- should only be set on a test method;
diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf
index 89a2aa5fe3..87669ddb46 100644
--- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf
+++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf
@@ -1,4 +1,4 @@
-
+
@@ -295,6 +295,51 @@ The type declaring these methods should also respect the following rules:
Verwenden Sie „Assert“ anstelle von „CollectionAssert“{Locked="CollectionAssert"}{Locked="Assert"}
+
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ {Locked="[MemberCondition]"}{Locked="public static"}{Locked="bool"}
+
+
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="public"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="static"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="bool"}
+
+
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ {0} is the containing type name. {1} is the method name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ {0} is the containing type name. {1} is the property name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' arguments should be valid
+ '[MemberCondition]' arguments should be valid
+ {Locked="[MemberCondition]"}
+ DataRow entry should have the following layout to be valid:
- should only be set on a test method;
diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf
index f71bc0367e..624caa85e9 100644
--- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf
+++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf
@@ -1,4 +1,4 @@
-
+
@@ -295,6 +295,51 @@ The type declaring these methods should also respect the following rules:
Usar "Assert" en lugar de "CollectionAssert"{Locked="CollectionAssert"}{Locked="Assert"}
+
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ {Locked="[MemberCondition]"}{Locked="public static"}{Locked="bool"}
+
+
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="public"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="static"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="bool"}
+
+
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ {0} is the containing type name. {1} is the method name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ {0} is the containing type name. {1} is the property name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' arguments should be valid
+ '[MemberCondition]' arguments should be valid
+ {Locked="[MemberCondition]"}
+ DataRow entry should have the following layout to be valid:
- should only be set on a test method;
diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf
index 236d5160c7..c0cc28c4ac 100644
--- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf
+++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf
@@ -1,4 +1,4 @@
-
+
@@ -295,6 +295,51 @@ The type declaring these methods should also respect the following rules:
Utilisez « Assert » au lieu de « CollectionAssert »{Locked="CollectionAssert"}{Locked="Assert"}
+
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ {Locked="[MemberCondition]"}{Locked="public static"}{Locked="bool"}
+
+
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="public"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="static"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="bool"}
+
+
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ {0} is the containing type name. {1} is the method name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ {0} is the containing type name. {1} is the property name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' arguments should be valid
+ '[MemberCondition]' arguments should be valid
+ {Locked="[MemberCondition]"}
+ DataRow entry should have the following layout to be valid:
- should only be set on a test method;
diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf
index efcf5c9cbb..edc65b275e 100644
--- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf
+++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf
@@ -1,4 +1,4 @@
-
+
@@ -295,6 +295,51 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti:
Usare "Assert" invece di "CollectionAssert"{Locked="CollectionAssert"}{Locked="Assert"}
+
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ {Locked="[MemberCondition]"}{Locked="public static"}{Locked="bool"}
+
+
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="public"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="static"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="bool"}
+
+
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ {0} is the containing type name. {1} is the method name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ {0} is the containing type name. {1} is the property name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' arguments should be valid
+ '[MemberCondition]' arguments should be valid
+ {Locked="[MemberCondition]"}
+ DataRow entry should have the following layout to be valid:
- should only be set on a test method;
diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf
index 9bd0e25a33..294a7b4f3a 100644
--- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf
+++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf
@@ -1,4 +1,4 @@
-
+
@@ -295,6 +295,51 @@ The type declaring these methods should also respect the following rules:
'CollectionAssert' の代わりに 'Assert' を使用する{Locked="CollectionAssert"}{Locked="Assert"}
+
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ {Locked="[MemberCondition]"}{Locked="public static"}{Locked="bool"}
+
+
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="public"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="static"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="bool"}
+
+
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ {0} is the containing type name. {1} is the method name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ {0} is the containing type name. {1} is the property name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' arguments should be valid
+ '[MemberCondition]' arguments should be valid
+ {Locked="[MemberCondition]"}
+ DataRow entry should have the following layout to be valid:
- should only be set on a test method;
diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf
index 68815c26a1..eecdcdbdb8 100644
--- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf
+++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf
@@ -1,4 +1,4 @@
-
+
@@ -295,6 +295,51 @@ The type declaring these methods should also respect the following rules:
'CollectionAssert' 대신 'Assert'를 사용하세요.{Locked="CollectionAssert"}{Locked="Assert"}
+
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ {Locked="[MemberCondition]"}{Locked="public static"}{Locked="bool"}
+
+
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="public"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="static"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="bool"}
+
+
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ {0} is the containing type name. {1} is the method name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ {0} is the containing type name. {1} is the property name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' arguments should be valid
+ '[MemberCondition]' arguments should be valid
+ {Locked="[MemberCondition]"}
+ DataRow entry should have the following layout to be valid:
- should only be set on a test method;
diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf
index c98ef31983..5fd7f07552 100644
--- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf
+++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf
@@ -1,4 +1,4 @@
-
+
@@ -295,6 +295,51 @@ The type declaring these methods should also respect the following rules:
Użyj instrukcji „Assert” zamiast elementu „CollectionAssert”{Locked="CollectionAssert"}{Locked="Assert"}
+
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ {Locked="[MemberCondition]"}{Locked="public static"}{Locked="bool"}
+
+
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="public"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="static"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="bool"}
+
+
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ {0} is the containing type name. {1} is the method name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ {0} is the containing type name. {1} is the property name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' arguments should be valid
+ '[MemberCondition]' arguments should be valid
+ {Locked="[MemberCondition]"}
+ DataRow entry should have the following layout to be valid:
- should only be set on a test method;
diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf
index 2ef921e5f3..68515e8476 100644
--- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf
+++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf
@@ -1,4 +1,4 @@
-
+
@@ -295,6 +295,51 @@ The type declaring these methods should also respect the following rules:
Usar 'Assert' em vez de 'CollectionAssert'{Locked="CollectionAssert"}{Locked="Assert"}
+
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ {Locked="[MemberCondition]"}{Locked="public static"}{Locked="bool"}
+
+
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="public"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="static"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="bool"}
+
+
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ {0} is the containing type name. {1} is the method name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ {0} is the containing type name. {1} is the property name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' arguments should be valid
+ '[MemberCondition]' arguments should be valid
+ {Locked="[MemberCondition]"}
+ DataRow entry should have the following layout to be valid:
- should only be set on a test method;
diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf
index 931012b83e..7b46e2628c 100644
--- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf
+++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf
@@ -1,4 +1,4 @@
-
+
@@ -295,6 +295,51 @@ The type declaring these methods should also respect the following rules:
Использование "Assert" вместо "CollectionAssert"{Locked="CollectionAssert"}{Locked="Assert"}
+
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ {Locked="[MemberCondition]"}{Locked="public static"}{Locked="bool"}
+
+
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="public"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="static"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="bool"}
+
+
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ {0} is the containing type name. {1} is the method name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ {0} is the containing type name. {1} is the property name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' arguments should be valid
+ '[MemberCondition]' arguments should be valid
+ {Locked="[MemberCondition]"}
+ DataRow entry should have the following layout to be valid:
- should only be set on a test method;
diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf
index 8714361ba3..a575d33ed4 100644
--- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf
+++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf
@@ -1,4 +1,4 @@
-
+
@@ -295,6 +295,51 @@ The type declaring these methods should also respect the following rules:
'CollectionAssert' yerine 'Assert' kullanın{Locked="CollectionAssert"}{Locked="Assert"}
+
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ {Locked="[MemberCondition]"}{Locked="public static"}{Locked="bool"}
+
+
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="public"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="static"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="bool"}
+
+
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ {0} is the containing type name. {1} is the method name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ {0} is the containing type name. {1} is the property name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' arguments should be valid
+ '[MemberCondition]' arguments should be valid
+ {Locked="[MemberCondition]"}
+ DataRow entry should have the following layout to be valid:
- should only be set on a test method;
diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf
index 5bd0b32459..449f14dff7 100644
--- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf
+++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf
@@ -1,4 +1,4 @@
-
+
@@ -295,6 +295,51 @@ The type declaring these methods should also respect the following rules:
使用 "Assert" 而非 "CollectionAssert"{Locked="CollectionAssert"}{Locked="Assert"}
+
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ {Locked="[MemberCondition]"}{Locked="public static"}{Locked="bool"}
+
+
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="public"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="static"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="bool"}
+
+
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ {0} is the containing type name. {1} is the method name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ {0} is the containing type name. {1} is the property name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' arguments should be valid
+ '[MemberCondition]' arguments should be valid
+ {Locked="[MemberCondition]"}
+ DataRow entry should have the following layout to be valid:
- should only be set on a test method;
diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf
index 494ee01b4c..afe1d93a42 100644
--- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf
+++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf
@@ -1,4 +1,4 @@
-
+
@@ -295,6 +295,51 @@ The type declaring these methods should also respect the following rules:
使用 'Assert' 而不是 'CollectionAssert'{Locked="CollectionAssert"}{Locked="Assert"}
+
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ The members referenced by a '[MemberCondition]' attribute must exist on the referenced type, be 'public static', return 'bool', and (for methods) be parameterless. The attribute throws at runtime when these rules are violated; this analyzer surfaces the same problems at build time so typos and refactors do not silently break test gating.
+ {Locked="[MemberCondition]"}{Locked="public static"}{Locked="bool"}
+
+
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ '[MemberCondition]' member '{0}.{1}' cannot be found
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'public'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="public"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ '[MemberCondition]' referenced member '{0}.{1}' must be 'static'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="static"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ '[MemberCondition]' referenced member '{0}.{1}' must be a property, field, or parameterless method
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ '[MemberCondition]' referenced member '{0}.{1}' must return 'bool'
+ {0} is the containing type name. {1} is the member name. {Locked="[MemberCondition]"}{Locked="bool"}
+
+
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ '[MemberCondition]' referenced method '{0}.{1}' must be parameterless
+ {0} is the containing type name. {1} is the method name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ '[MemberCondition]' referenced property '{0}.{1}' must have a getter
+ {0} is the containing type name. {1} is the property name. {Locked="[MemberCondition]"}
+
+
+ '[MemberCondition]' arguments should be valid
+ '[MemberCondition]' arguments should be valid
+ {Locked="[MemberCondition]"}
+ DataRow entry should have the following layout to be valid:
- should only be set on a test method;
diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/MemberConditionShouldBeValidAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/MemberConditionShouldBeValidAnalyzerTests.cs
new file mode 100644
index 0000000000..1532038c3f
--- /dev/null
+++ b/test/UnitTests/MSTest.Analyzers.UnitTests/MemberConditionShouldBeValidAnalyzerTests.cs
@@ -0,0 +1,579 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier<
+ MSTest.Analyzers.MemberConditionShouldBeValidAnalyzer,
+ Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>;
+
+namespace MSTest.Analyzers.Test;
+
+[TestClass]
+public sealed class MemberConditionShouldBeValidAnalyzerTests
+{
+ [TestMethod]
+ public async Task WhenMemberIsValidPublicStaticBoolProperty_NoDiagnostic()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ public static bool IsTrue => true;
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [MemberCondition(typeof(Conditions), nameof(Conditions.IsTrue))]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(code);
+ }
+
+ [TestMethod]
+ public async Task WhenMemberIsValidPublicStaticBoolField_NoDiagnostic()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ public static readonly bool IsTrue = true;
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [MemberCondition(typeof(Conditions), nameof(Conditions.IsTrue))]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(code);
+ }
+
+ [TestMethod]
+ public async Task WhenMemberIsValidPublicStaticBoolMethod_NoDiagnostic()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ public static bool IsTrue() => true;
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [MemberCondition(typeof(Conditions), nameof(Conditions.IsTrue))]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(code);
+ }
+
+ [TestMethod]
+ public async Task WhenAttributeIsOnTestClass_StillValidated()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ public static bool IsTrue => true;
+ }
+
+ [{|#0:MemberCondition(typeof(Conditions), "DoesNotExist")|}]
+ [TestClass]
+ public class MyTestClass
+ {
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(
+ code,
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MemberNotFoundRule)
+ .WithLocation(0)
+ .WithArguments("Conditions", "DoesNotExist"));
+ }
+
+ [TestMethod]
+ public async Task WhenMemberDoesNotExist_MemberNotFound()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ public static bool IsTrue => true;
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [{|#0:MemberCondition(typeof(Conditions), "DoesNotExist")|}]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(
+ code,
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MemberNotFoundRule)
+ .WithLocation(0)
+ .WithArguments("Conditions", "DoesNotExist"));
+ }
+
+ [TestMethod]
+ public async Task WhenMemberIsInternal_MemberNotPublic()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ internal static bool InternalIsTrue => true;
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [{|#0:MemberCondition(typeof(Conditions), nameof(Conditions.InternalIsTrue))|}]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(
+ code,
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MemberNotPublicRule)
+ .WithLocation(0)
+ .WithArguments("Conditions", "InternalIsTrue"));
+ }
+
+ [TestMethod]
+ public async Task WhenPropertyIsInstance_MemberNotStatic()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public class Conditions
+ {
+ public bool InstanceIsTrue => true;
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [{|#0:MemberCondition(typeof(Conditions), nameof(Conditions.InstanceIsTrue))|}]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(
+ code,
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MemberNotStaticRule)
+ .WithLocation(0)
+ .WithArguments("Conditions", "InstanceIsTrue"));
+ }
+
+ [TestMethod]
+ public async Task WhenPropertyReturnsNonBool_MemberWrongReturnType()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ public static int NotBool => 42;
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [{|#0:MemberCondition(typeof(Conditions), nameof(Conditions.NotBool))|}]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(
+ code,
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MemberWrongReturnTypeRule)
+ .WithLocation(0)
+ .WithArguments("Conditions", "NotBool"));
+ }
+
+ [TestMethod]
+ public async Task WhenMethodHasParameters_MethodHasParameters()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ public static bool WithParam(int x) => x > 0;
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [{|#0:MemberCondition(typeof(Conditions), nameof(Conditions.WithParam))|}]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(
+ code,
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MethodHasParametersRule)
+ .WithLocation(0)
+ .WithArguments("Conditions", "WithParam"));
+ }
+
+ [TestMethod]
+ public async Task WhenMethodReturnsNonBool_MemberWrongReturnType()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ public static int NotBoolMethod() => 0;
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [{|#0:MemberCondition(typeof(Conditions), nameof(Conditions.NotBoolMethod))|}]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(
+ code,
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MemberWrongReturnTypeRule)
+ .WithLocation(0)
+ .WithArguments("Conditions", "NotBoolMethod"));
+ }
+
+ [TestMethod]
+ public async Task WhenPropertyIsWriteOnly_PropertyNotReadable()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ public static bool WriteOnly { set { } }
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [{|#0:MemberCondition(typeof(Conditions), nameof(Conditions.WriteOnly))|}]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(
+ code,
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.PropertyNotReadableRule)
+ .WithLocation(0)
+ .WithArguments("Conditions", "WriteOnly"));
+ }
+
+ [TestMethod]
+ public async Task WhenFieldIsNonBool_MemberWrongReturnType()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ public static readonly string NotBoolField = "x";
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [{|#0:MemberCondition(typeof(Conditions), nameof(Conditions.NotBoolField))|}]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(
+ code,
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MemberWrongReturnTypeRule)
+ .WithLocation(0)
+ .WithArguments("Conditions", "NotBoolField"));
+ }
+
+ [TestMethod]
+ public async Task WhenAdditionalMembersAreInvalid_AllReported()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ public static bool IsTrue => true;
+ public static int NotBool => 0;
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [{|#0:MemberCondition(typeof(Conditions), nameof(Conditions.IsTrue), "Missing", nameof(Conditions.NotBool))|}]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(
+ code,
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MemberNotFoundRule)
+ .WithLocation(0)
+ .WithArguments("Conditions", "Missing"),
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MemberWrongReturnTypeRule)
+ .WithLocation(0)
+ .WithArguments("Conditions", "NotBool"));
+ }
+
+ [TestMethod]
+ public async Task WhenExplicitConditionMode_StillValidated()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ public static bool IsTrue => true;
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [{|#0:MemberCondition(ConditionMode.Exclude, typeof(Conditions), "DoesNotExist")|}]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(
+ code,
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MemberNotFoundRule)
+ .WithLocation(0)
+ .WithArguments("Conditions", "DoesNotExist"));
+ }
+
+ [TestMethod]
+ public async Task WhenInheritedPublicStaticMember_NoDiagnostic()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public class BaseConditions
+ {
+ public static bool InheritedIsTrue => true;
+ }
+
+ public class DerivedConditions : BaseConditions
+ {
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [MemberCondition(typeof(DerivedConditions), nameof(DerivedConditions.InheritedIsTrue))]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(code);
+ }
+
+ [TestMethod]
+ public async Task WhenMemberIsEvent_MemberWrongKind()
+ {
+ string code = """
+ using System;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ public static event EventHandler SomeEvent;
+
+ public static void Raise() => SomeEvent?.Invoke(null, EventArgs.Empty);
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [{|#0:MemberCondition(typeof(Conditions), nameof(Conditions.SomeEvent))|}]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(
+ code,
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MemberWrongKindRule)
+ .WithLocation(0)
+ .WithArguments("Conditions", "SomeEvent"));
+ }
+
+ [TestMethod]
+ public async Task WhenFieldIsInstance_MemberNotStatic()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public class Conditions
+ {
+ public bool InstanceField = true;
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [{|#0:MemberCondition(typeof(Conditions), nameof(Conditions.InstanceField))|}]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(
+ code,
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MemberNotStaticRule)
+ .WithLocation(0)
+ .WithArguments("Conditions", "InstanceField"));
+ }
+
+ [TestMethod]
+ public async Task WhenMultipleConditionAttributes_EachValidated()
+ {
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ public static bool IsTrue => true;
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [{|#0:MemberCondition(typeof(Conditions), "First")|}]
+ [{|#1:MemberCondition(typeof(Conditions), "Second")|}]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(
+ code,
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MemberNotFoundRule)
+ .WithLocation(0)
+ .WithArguments("Conditions", "First"),
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MemberNotFoundRule)
+ .WithLocation(1)
+ .WithArguments("Conditions", "Second"));
+ }
+
+ [TestMethod]
+ public async Task WhenMethodHasParameterlessAndParameterizedOverloads_PicksParameterless_NoDiagnostic()
+ {
+ // Runtime binding uses Type.GetMethod(name, ..., types: Type.EmptyTypes), which selects
+ // the parameterless overload. The analyzer must not falsely flag this just because the
+ // parameterized overload comes first in declaration order.
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public static class Conditions
+ {
+ public static bool IsTrue(int unused) => true;
+ public static bool IsTrue() => true;
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [MemberCondition(typeof(Conditions), nameof(Conditions.IsTrue))]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(code);
+ }
+
+ [TestMethod]
+ public async Task WhenDerivedHasInstanceMemberShadowingBaseStatic_PicksBaseStatic_NoDiagnostic()
+ {
+ // Runtime FlattenHierarchy + Public + Static binds to Base.IsTrue (the static one),
+ // not Derived.IsTrue (the instance one). The analyzer must mirror that.
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ public class Base
+ {
+ public static bool IsTrue => true;
+ }
+
+ public class Derived : Base
+ {
+ public new bool IsTrue => false; // instance, shadows the static base member
+ }
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [MemberCondition(typeof(Derived), nameof(Derived.IsTrue))]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(code);
+ }
+
+ [TestMethod]
+ public async Task WhenConditionTypeIsArrayType_MemberNotFound()
+ {
+ // typeof(int[]) is an IArrayTypeSymbol, not INamedTypeSymbol. The runtime would still throw
+ // InvalidOperationException because int[] has no user-declared static bool members; the
+ // analyzer must surface MSTEST0070 rather than silently skipping the attribute.
+ string code = """
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class MyTestClass
+ {
+ [{|#0:MemberCondition(typeof(int[]), "AnyName")|}]
+ [TestMethod]
+ public void TestMethod() { }
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(
+ code,
+ VerifyCS.Diagnostic(MemberConditionShouldBeValidAnalyzer.MemberNotFoundRule)
+ .WithLocation(0)
+ .WithArguments("int[]", "AnyName"));
+ }
+}