diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Helpers/AttributeHelpers.cs b/src/Adapter/MSTestAdapter.PlatformServices/Helpers/AttributeHelpers.cs index a9fc301f7c..8c1695de61 100644 --- a/src/Adapter/MSTestAdapter.PlatformServices/Helpers/AttributeHelpers.cs +++ b/src/Adapter/MSTestAdapter.PlatformServices/Helpers/AttributeHelpers.cs @@ -11,29 +11,13 @@ public static bool IsIgnored(this ICustomAttributeProvider type, out string? ign { Attribute[] allAttributes = ReflectHelper.Instance.GetCustomAttributesCached(type); - // Fast path: no ConditionBaseAttribute present (common case) → zero allocations - bool hasConditionAttribute = false; - foreach (Attribute attr in allAttributes) - { - if (attr is ConditionBaseAttribute) - { - hasConditionAttribute = true; - break; - } - } - - if (!hasConditionAttribute) - { - ignoreMessage = null; - return false; - } - - // Slow path: manual grouping instead of LINQ GroupBy to avoid iterator/Lookup allocations. + // Single pass: build groups while iterating. For the common case (no ConditionBaseAttribute) + // the dictionary and list are never allocated → zero heap allocations. // We track insertion order explicitly because Dictionary.Values enumeration // order is not guaranteed (especially on .NET Framework), and we need to return the ignore // message from the first unsatisfied group in attribute order. - Dictionary groups = []; - List groupOrder = []; + Dictionary? groups = null; + List? groupOrder = null; foreach (Attribute attr in allAttributes) { if (attr is not ConditionBaseAttribute conditionAttr) @@ -41,6 +25,9 @@ public static bool IsIgnored(this ICustomAttributeProvider type, out string? ign continue; } + groups ??= []; + groupOrder ??= []; + bool shouldRun = conditionAttr.Mode == ConditionMode.Include ? conditionAttr.IsConditionMet : !conditionAttr.IsConditionMet; if (!groups.TryGetValue(conditionAttr.GroupName, out (bool Satisfied, string? FirstMessage) groupState)) @@ -61,7 +48,13 @@ public static bool IsIgnored(this ICustomAttributeProvider type, out string? ign } } - foreach (string groupName in groupOrder) + if (groups is null) + { + ignoreMessage = null; + return false; + } + + foreach (string groupName in groupOrder!) { (bool satisfied, string? firstMessage) = groups[groupName]; if (!satisfied)