From 2a51ba6dd9f4f65a6a676b3644170fefa77c03e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1sp=C3=A1r=20Nagy?= Date: Tue, 13 Jan 2026 14:50:13 +0100 Subject: [PATCH 1/6] Handle invalid scope tag expressions, allow receiving invalid hooks and scopes --- .../Discovery/ReqnrollDiscoverer.cs | 6 +- .../ReqnrollProxies/Data/BindingScopeData.cs | 1 + .../ReqnrollProxies/Data/HookData.cs | 1 + .../ReqnrollProxies/HookBindingAdapter.cs | 2 + .../StepDefinitionBindingAdapter.cs | 2 + .../Hook.cs | 4 +- .../StepScope.cs | 4 +- .../Discovery/BindingImporter.cs | 42 ++++++++--- .../Discovery/DiscoveryInvoker.cs | 25 +++++++ .../Discovery/ProjectBindingRegistry.cs | 2 +- .../Discovery/ProjectHookBinding.cs | 6 +- .../Discovery/ProjectStepDefinitionBinding.cs | 2 +- Reqnroll.VisualStudio/Discovery/Scope.cs | 8 ++ .../Discovery/BindingImporterTests.cs | 74 ++++++++++++++++++- 14 files changed, 161 insertions(+), 18 deletions(-) diff --git a/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/Discovery/ReqnrollDiscoverer.cs b/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/Discovery/ReqnrollDiscoverer.cs index d627b0d2..f313ae1b 100644 --- a/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/Discovery/ReqnrollDiscoverer.cs +++ b/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/Discovery/ReqnrollDiscoverer.cs @@ -93,7 +93,8 @@ private Hook CreateHook(HookBindingAdapter sdb, HookOrder = sdb.HookOrder, Method = sdb.Method.ToString(), Scope = GetScope(sdb), - SourceLocation = sourceLocation.Reduce((string)null!) + SourceLocation = sourceLocation.Reduce((string)null!), + Error = sdb.Error }; return stepDefinition; @@ -137,7 +138,8 @@ private string GetParamType(string parameterTypeName, Func getKe { Tag = scopedBinding.BindingScopeTag, FeatureTitle = scopedBinding.BindingScopeFeatureTitle, - ScenarioTitle = scopedBinding.BindingScopeScenarioTitle + ScenarioTitle = scopedBinding.BindingScopeScenarioTitle, + Error = scopedBinding.BindingScopeError }; } diff --git a/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/Data/BindingScopeData.cs b/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/Data/BindingScopeData.cs index c04e2854..3e3de76e 100644 --- a/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/Data/BindingScopeData.cs +++ b/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/Data/BindingScopeData.cs @@ -6,4 +6,5 @@ public class BindingScopeData public string Tag { get; set; } // contains leading '@', e.g. '@mytag' public string FeatureTitle { get; set; } public string ScenarioTitle { get; set; } + public string Error { get; set; } } diff --git a/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/Data/HookData.cs b/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/Data/HookData.cs index c2fd72f4..d8e538cf 100644 --- a/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/Data/HookData.cs +++ b/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/Data/HookData.cs @@ -6,4 +6,5 @@ public class HookData public BindingScopeData Scope { get; set; } public string Type { get; set; } public int? HookOrder { get; set; } + public string Error { get; set; } } diff --git a/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/HookBindingAdapter.cs b/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/HookBindingAdapter.cs index 1bba025c..bb8d4350 100644 --- a/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/HookBindingAdapter.cs +++ b/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/HookBindingAdapter.cs @@ -10,5 +10,7 @@ public record HookBindingAdapter(HookData Adaptee) : IScopedBindingAdapter public string? BindingScopeTag => Adaptee.Scope?.Tag; public string? BindingScopeFeatureTitle => Adaptee.Scope?.FeatureTitle; public string? BindingScopeScenarioTitle => Adaptee.Scope?.ScenarioTitle; + public string? BindingScopeError => Adaptee.Scope?.Error; public int? HookOrder => Adaptee.HookOrder; + public string? Error => Adaptee.Error; } \ No newline at end of file diff --git a/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/StepDefinitionBindingAdapter.cs b/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/StepDefinitionBindingAdapter.cs index 30f8b7e9..7545af43 100644 --- a/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/StepDefinitionBindingAdapter.cs +++ b/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Generic/ReqnrollProxies/StepDefinitionBindingAdapter.cs @@ -8,6 +8,7 @@ public interface IScopedBindingAdapter string? BindingScopeTag { get; } string? BindingScopeFeatureTitle { get; } string? BindingScopeScenarioTitle { get; } + string? BindingScopeError { get; } } public record StepDefinitionBindingAdapter(StepDefinitionData Adaptee) : IScopedBindingAdapter @@ -22,6 +23,7 @@ public record StepDefinitionBindingAdapter(StepDefinitionData Adaptee) : IScoped public string? BindingScopeTag => Adaptee.Scope?.Tag; public string? BindingScopeFeatureTitle => Adaptee.Scope?.FeatureTitle; public string? BindingScopeScenarioTitle => Adaptee.Scope?.ScenarioTitle; + public string? BindingScopeError => Adaptee.Scope?.Error; public virtual Option GetProperty(string propertyName) { return Adaptee.ReflectionHasProperty(propertyName) ? Adaptee.ReflectionGetProperty(propertyName) : None.Value; diff --git a/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Models/Hook.cs b/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Models/Hook.cs index d682489f..0e231948 100644 --- a/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Models/Hook.cs +++ b/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Models/Hook.cs @@ -9,13 +9,15 @@ public class Hook //public string ParamTypes { get; set; } public StepScope Scope { get; set; } + public string Error { get; set; } + public string SourceLocation { get; set; } #region Equality protected bool Equals(Hook other) { - return Type == other.Type && HookOrder == other.HookOrder && Method == other.Method && Equals(Scope, other.Scope) && SourceLocation == other.SourceLocation; + return Type == other.Type && HookOrder == other.HookOrder && Method == other.Method && Equals(Scope, other.Scope) && Error == other.Error && SourceLocation == other.SourceLocation; } public override bool Equals(object obj) diff --git a/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Models/StepScope.cs b/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Models/StepScope.cs index e3753acb..b7c22e0f 100644 --- a/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Models/StepScope.cs +++ b/Connectors/Reqnroll.VisualStudio.ReqnrollConnector.Models/StepScope.cs @@ -6,12 +6,14 @@ public class StepScope public string Tag { get; set; } public string FeatureTitle { get; set; } public string ScenarioTitle { get; set; } + public string Error { get; set; } #region Equality protected bool Equals(StepScope other) => string.Equals(Tag, other.Tag) && string.Equals(FeatureTitle, other.FeatureTitle) && - string.Equals(ScenarioTitle, other.ScenarioTitle); + string.Equals(ScenarioTitle, other.ScenarioTitle) && + string.Equals(Error, other.Error); public override bool Equals(object obj) { diff --git a/Reqnroll.VisualStudio/Discovery/BindingImporter.cs b/Reqnroll.VisualStudio/Discovery/BindingImporter.cs index 6b2e03b2..130a3368 100644 --- a/Reqnroll.VisualStudio/Discovery/BindingImporter.cs +++ b/Reqnroll.VisualStudio/Discovery/BindingImporter.cs @@ -46,7 +46,7 @@ public ProjectStepDefinitionBinding ImportStepDefinition(StepDefinition stepDefi } return new ProjectStepDefinitionBinding(stepDefinitionType, regex, scope, implementation, - stepDefinition.Expression, stepDefinition.Error); + stepDefinition.Expression, GetBindingError(stepDefinition.Error, scope, "step definition")); } catch (Exception ex) { @@ -72,7 +72,7 @@ public ProjectHookBinding ImportHook(Hook hook) _implementations.Add(hook.Method, implementation); } - return new ProjectHookBinding(implementation, scope, hookType, hook.HookOrder); + return new ProjectHookBinding(implementation, scope, hookType, hook.HookOrder, GetBindingError(hook.Error, scope, "hook")); } catch (Exception ex) { @@ -81,6 +81,15 @@ public ProjectHookBinding ImportHook(Hook hook) } } + private string GetBindingError(string error, Scope scope, string bindingType) + { + if (!string.IsNullOrWhiteSpace(error)) + return $"Invalid {bindingType}: {error}"; + if (!string.IsNullOrWhiteSpace(scope?.Error)) + return $"Invalid scope for {bindingType}: {scope.Error}"; + return null; + } + private static Regex ParseRegex(StepDefinition stepDefinition) => string.IsNullOrEmpty(stepDefinition.Regex) ? null @@ -149,13 +158,28 @@ private Scope ParseScope(StepScope bindingScope) if (bindingScope == null) return null; - return new Scope + try + { + return new Scope + { + FeatureTitle = bindingScope.FeatureTitle, + ScenarioTitle = bindingScope.ScenarioTitle, + Tag = string.IsNullOrWhiteSpace(bindingScope.Tag) + ? null + : _tagExpressionParser.Parse(bindingScope.Tag), + Error = bindingScope.Error + }; + } + catch (Exception ex) { - FeatureTitle = bindingScope.FeatureTitle, - ScenarioTitle = bindingScope.ScenarioTitle, - Tag = string.IsNullOrWhiteSpace(bindingScope.Tag) - ? null - : _tagExpressionParser.Parse(bindingScope.Tag) - }; + _logger.LogVerbose($"Invalid tag expression '{bindingScope.Tag}': {ex.Message}"); + return new Scope + { + FeatureTitle = bindingScope.FeatureTitle, + ScenarioTitle = bindingScope.ScenarioTitle, + Tag = null, + Error = $"Invalid tag expression '{bindingScope.Tag}': {ex.Message}" + }; + } } } diff --git a/Reqnroll.VisualStudio/Discovery/DiscoveryInvoker.cs b/Reqnroll.VisualStudio/Discovery/DiscoveryInvoker.cs index 379663bc..b4e16ded 100644 --- a/Reqnroll.VisualStudio/Discovery/DiscoveryInvoker.cs +++ b/Reqnroll.VisualStudio/Discovery/DiscoveryInvoker.cs @@ -143,6 +143,7 @@ public IDiscovery ThenImportBindings(string projectName) $"{_stepDefinitions.Length} step definitions and {_hooks.Length} hooks discovered for project {projectName}"); ReportInvalidStepDefinitions(); + ReportInvalidHooks(); return this; } @@ -193,6 +194,30 @@ private void ReportInvalidStepDefinitions() }) ); } + + + private void ReportInvalidHooks() + { + if (!_hooks.Any(h => !h.IsValid)) + return; + + _logger.LogWarning($"Invalid hooks found: {Environment.NewLine}" + + string.Join(Environment.NewLine, _hooks + .Where(h => !h.IsValid) + .Select(h => + $" {h}: {h.Error} at {h.Implementation?.SourceLocation}"))); + + _errorListServices.AddErrors( + _hooks.Where(h => !h.IsValid) + .Select(h => new DeveroomUserError + { + Category = DeveroomUserErrorCategory.Discovery, + Message = h.Error, + SourceLocation = h.Implementation?.SourceLocation, + Type = TaskErrorCategory.Error + }) + ); + } } private class FailedDiscovery : IDiscovery diff --git a/Reqnroll.VisualStudio/Discovery/ProjectBindingRegistry.cs b/Reqnroll.VisualStudio/Discovery/ProjectBindingRegistry.cs index 103413a4..0657a3d5 100644 --- a/Reqnroll.VisualStudio/Discovery/ProjectBindingRegistry.cs +++ b/Reqnroll.VisualStudio/Discovery/ProjectBindingRegistry.cs @@ -34,7 +34,7 @@ public ProjectBindingRegistry(IEnumerable stepDefi public HookMatchResult MatchScenarioToHooks(Scenario scenario, IGherkinDocumentContext context) { var hookMatches = Hooks - .Where(h => h.Match(scenario, context)) + .Where(h => h.IsValid && h.Match(scenario, context)) .OrderBy(h => h.HookType) .ThenBy(h => h.HookOrder) .ToArray(); diff --git a/Reqnroll.VisualStudio/Discovery/ProjectHookBinding.cs b/Reqnroll.VisualStudio/Discovery/ProjectHookBinding.cs index fc3ab57d..a33886c1 100644 --- a/Reqnroll.VisualStudio/Discovery/ProjectHookBinding.cs +++ b/Reqnroll.VisualStudio/Discovery/ProjectHookBinding.cs @@ -8,11 +8,15 @@ public class ProjectHookBinding : ProjectBinding public HookType HookType { get; } public int HookOrder { get; } - public ProjectHookBinding(ProjectBindingImplementation implementation, Scope scope, HookType hookType, int? hookOrder) + public bool IsValid => Error == null && Scope?.IsValid != false; + public string Error { get; } + + public ProjectHookBinding(ProjectBindingImplementation implementation, Scope scope, HookType hookType, int? hookOrder, string error) : base(implementation, scope) { HookType = hookType; HookOrder = hookOrder ?? DefaultHookOrder; + Error = error; } public bool Match(Scenario scenario, IGherkinDocumentContext context) diff --git a/Reqnroll.VisualStudio/Discovery/ProjectStepDefinitionBinding.cs b/Reqnroll.VisualStudio/Discovery/ProjectStepDefinitionBinding.cs index 2774b6b9..74482e52 100644 --- a/Reqnroll.VisualStudio/Discovery/ProjectStepDefinitionBinding.cs +++ b/Reqnroll.VisualStudio/Discovery/ProjectStepDefinitionBinding.cs @@ -14,7 +14,7 @@ public ProjectStepDefinitionBinding(ScenarioBlock stepDefinitionType, Regex rege Error = error; } - public bool IsValid => Regex != null && Error == null; + public bool IsValid => Regex != null && Error == null && Scope?.IsValid != false; public string Error { get; } public ScenarioBlock StepDefinitionType { get; } public string SpecifiedExpression { get; } diff --git a/Reqnroll.VisualStudio/Discovery/Scope.cs b/Reqnroll.VisualStudio/Discovery/Scope.cs index cac8b351..5dfd8416 100644 --- a/Reqnroll.VisualStudio/Discovery/Scope.cs +++ b/Reqnroll.VisualStudio/Discovery/Scope.cs @@ -7,6 +7,9 @@ public class Scope public ITagExpression? Tag { get; set; } public string? FeatureTitle { get; set; } public string? ScenarioTitle { get; set; } + public string? Error { get; set; } + + public bool IsValid => Error == null; public override string ToString() { @@ -21,6 +24,11 @@ public override string ToString() result = result.Length > 0 ? result + ", " : result; result = $"{result}Scenario='{ScenarioTitle}'"; } + if (Error != null) + { + result = result.Length > 0 ? result + ", " : result; + result = $"{result}Error='{Error}'"; + } return result; } } diff --git a/Tests/Reqnroll.VisualStudio.Tests/Discovery/BindingImporterTests.cs b/Tests/Reqnroll.VisualStudio.Tests/Discovery/BindingImporterTests.cs index 0c8eed19..3d7457da 100644 --- a/Tests/Reqnroll.VisualStudio.Tests/Discovery/BindingImporterTests.cs +++ b/Tests/Reqnroll.VisualStudio.Tests/Discovery/BindingImporterTests.cs @@ -142,7 +142,7 @@ public void Imports_expression_and_error() expression: "my step", error: "this is an error")); result.SpecifiedExpression.Should().Be("my step"); - result.Error.Should().Be("this is an error"); + result.Error.Should().Be("Invalid step definition: this is an error"); result.IsValid.Should().BeFalse(); } @@ -155,7 +155,7 @@ public void Imports_invalid_step_definition_with_null_regex() var result = sut.ImportStepDefinition(stepDefinition); result.Regex.Should().BeNull(); - result.Error.Should().Be("this is an error"); + result.Error.Should().Be("Invalid step definition: this is an error"); result.IsValid.Should().BeFalse(); } @@ -264,4 +264,74 @@ public void Merges_implementations() result1.Implementation.Should().BeSameAs(result2.Implementation); } + + [Fact] + public void Parses_step_definition_with_invalid_tag_scope() + { + var sut = CreateSut(); + var result = sut.ImportStepDefinition(CreateStepDefinition(scope: new StepScope { Tag = "@foo ( wrong" })); + + result.Should().NotBeNull(); + result.IsValid.Should().BeFalse(); + result.Scope.Should().NotBeNull(); + result.Scope.IsValid.Should().BeFalse(); + result.Scope.Error.Should().Contain("Invalid tag expression"); + result.Error.Should().Contain("Invalid tag expression"); + } + + [Fact] + public void Parses_hook_with_invalid_tag_scope() + { + var sut = CreateSut(); + var result = sut.ImportHook(CreateHook(scope: new StepScope { Tag = "@foo ( wrong" })); + + result.Should().NotBeNull(); + result.IsValid.Should().BeFalse(); + result.Scope.Should().NotBeNull(); + result.Scope.IsValid.Should().BeFalse(); + result.Scope.Error.Should().Contain("Invalid tag expression"); + result.Error.Should().Contain("Invalid tag expression"); + } + + [Fact] + public void Parses_step_definition_with_invalid_tag_scope_and_source_error() + { + var sut = CreateSut(); + var result = sut.ImportStepDefinition(CreateStepDefinition( + scope: new StepScope { Tag = "@foo ( wrong" }, + error: "source error")); + + result.Should().NotBeNull(); + result.IsValid.Should().BeFalse(); + result.Error.Should().Be("Invalid step definition: source error"); + result.Scope.Should().NotBeNull(); + result.Scope.IsValid.Should().BeFalse(); + result.Scope.Error.Should().Contain("Invalid tag expression"); + } + + [Fact] + public void Parses_hook_with_invalid_tag_scope_and_source_error() + { + var sut = CreateSut(); + var result = sut.ImportHook(CreateHook(scope: new StepScope { Tag = "@foo ( wrong" }, error: "source error")); + + result.Should().NotBeNull(); + result.IsValid.Should().BeFalse(); + result.Error.Should().Be("Invalid hook: source error"); + result.Scope.Should().NotBeNull(); + result.Scope.IsValid.Should().BeFalse(); + result.Scope.Error.Should().Contain("Invalid tag expression"); + } + + private Hook CreateHook(string type = null, string sourceLocation = null, + StepScope scope = null, string method = null, int? hookOrder = null, string error = null) => + new() + { + Method = method ?? "M1", + Type = type ?? "BeforeScenario", + SourceLocation = sourceLocation, + Scope = scope, + HookOrder = hookOrder, + Error = error + }; } From 90f1b732809467b475df0c1e4afbad65d9c4df64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1sp=C3=A1r=20Nagy?= Date: Tue, 13 Jan 2026 15:16:43 +0100 Subject: [PATCH 2/6] rebuild net10 connector --- ....VisualStudio.ReqnrollConnector.Models.dll | Bin 13312 -> 13824 bytes .../reqnroll-vs.deps.json | 14 +++++++------- .../Reqnroll-Generic-net10.0/reqnroll-vs.dll | Bin 94720 -> 95232 bytes .../Reqnroll-Generic-net10.0/reqnroll-vs.exe | Bin 162816 -> 162304 bytes .../reqnroll-vs.runtimeconfig.json | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Connectors/prebuilt/Reqnroll-Generic-net10.0/Reqnroll.VisualStudio.ReqnrollConnector.Models.dll b/Connectors/prebuilt/Reqnroll-Generic-net10.0/Reqnroll.VisualStudio.ReqnrollConnector.Models.dll index 0dfb2537f97f0e312bd1f4d488a67400d9504840..6fe5a6c9aac3a3763ee2ada6ce5b3cf7a4706845 100644 GIT binary patch delta 2510 zcmZ9O3v5$W7{~wj_IADPHtbs3m5q(AW$UHwO4n{|6AVL8=m-ya$fgOQj7^EtDK7~q zEsr1wOzV+}#0e@fN_>EgV4@g8(4a^l#+mrcf(D;xR8ZrL`kix6HSsRzf6woKzMXTv z@0_&F-k$A!ct@Zy_2*H@XBYv0u@VQOWay?j(4IRSQ!mZUeOxzcT01b0+slB_)?AId zBny-fYbjt6I~s|yP9@}cB21Q_T9iGgM3fjiri7G6_PG+MUrRIaXBIUTR_r!_-^T%V z;r=JvC?Lxklwf|o`j+CWRyTTSm2g0{+%GzSJ8nUaRcT|C`&hFUpEX-GCICg2KCXrf za70^FBO)xWhKF0Vs!=a2u4a@=H6{v+tKniXJzQK&yE7Vy!(bP*Ocfuhw|FDPW^A92 z=1PaFRl`XgcE5FfDebVju)}Khlhu=H5|eTj>$Gm;DI3kgqlZRgKBQ z;%Zuy+;zg@YGy*oO%)bbGm4U%CM>QV)1&OB3y-UrU5rjlHEtlLb8^R3T$FWJ4Zd5M zC+~_p*)_J8YW;f@;1+W(8Nes}Um-rU1v6BLnnEE&hm1hM5FK;TIsYz7cZZIRt1(6% z+iYQRH7&}F;wg%&X;ETy70Kc%tgttH%)%z=q0XDg3X9aJCmz+{^UIy{uGo1YE548% z<6SeW!XRH#4$jFR*F(ybI!b4g5W2nEUqu}hv6@cbvB~7l0vi?}k`3|fW2={tn>AR4 zd6XV2fc%f7h+Z=KsOA*nTu01=-pAt1{{f6 zS_QNQXkt)_L91W`HIjPC+b&rN)Uh z6rO^Co7wS_joJeBD}}{NXK4x+sQGoJ18U|uy&o==(BT2w^;dK!kw;RL#@wiqG?m8O zFa&MG*Y$hL!kD60giy{4I%BW-RpaW}SFts^FBwD^rEBf_P<~QLAxUkXe~=xFU&H z#*d4&|5heL_^pY$)z(k+v!qg+lW0Uz3ze+~7P^@Hm{L@=8k9)7g~kFHEvc8r0;rP2 zlL(?#5>FzCxFEjK=he)3>=bD3v=*;P;ybN_hhIZ%7iU-pzo4A&l>V|>haP@aQJ__V z3XvNGZKdm(r^v06oJ4#I1%DSwW%FEJ>~2>P`@vPsF1V)1ehKrrFO=@aZeOz1(OCxs3Rg_TDtRu_GUk9cZr!-By=-cEg)&d0Jk+uq zKImGwVEN2>JM+JfKViACF&2-}H+GwX$%%NPDb^Tk`Kuhel?ee>PIqeQ4>X zck91>p?}Jf#K8}?Ec^Vry{^H^b?ah>HXh#JvOo0ckr(eeaH#V7k=YmTJzZ8|ExK~+ z(t%Vc_@;I9M?ZZRTXW`t&b4jlpFQ6_dmtG*`;M)4TNzvfb1DAK zo=-MEb*k;`?UP<^-bLq*xvBN1q0E`DQvVdS4*In2i0~2fQRwBS#xF?){hFwVvbo`a zGdy^;?A+Di>5tv%-j9`@D9eUlO-PfKOOxiF@wBltwd=_K6N?&@jVtA^5kB))-uLHf zyz1I^mWe!+_a{axY*ciEU85u55n4VM9a5CW{B5xj<&H+g=pYM!_2@+(x{#)K1e4G} zBQ1y{fo7~g551Gr(&&~WX&OzEzntQ_DWZ$wn#BL}$TUSIsr67)E0XM7yvfeji}S|H V62IEp%->!6uFaPvepGUC+ds-1(v1KB delta 2188 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 diff --git a/Connectors/prebuilt/Reqnroll-Generic-net10.0/reqnroll-vs.exe b/Connectors/prebuilt/Reqnroll-Generic-net10.0/reqnroll-vs.exe index 0e3a0bd03353e189978b90296843212803c00174..214e9946beb63d23e58a89894f1ce13e8308916e 100644 GIT binary patch delta 16806 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 delta 16910 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 diff --git a/Connectors/prebuilt/Reqnroll-Generic-net10.0/reqnroll-vs.runtimeconfig.json b/Connectors/prebuilt/Reqnroll-Generic-net10.0/reqnroll-vs.runtimeconfig.json index 397bcde1..f730443c 100644 --- a/Connectors/prebuilt/Reqnroll-Generic-net10.0/reqnroll-vs.runtimeconfig.json +++ b/Connectors/prebuilt/Reqnroll-Generic-net10.0/reqnroll-vs.runtimeconfig.json @@ -3,7 +3,7 @@ "tfm": "net10.0", "framework": { "name": "Microsoft.NETCore.App", - "version": "10.0.0-rc.2.25502.107" + "version": "10.0.0" }, "configProperties": { "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, From 448f08625603fc258787f9a8f6ad998e8cf9fabf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1sp=C3=A1r=20Nagy?= Date: Tue, 13 Jan 2026 15:31:37 +0100 Subject: [PATCH 3/6] move IsValid and Error to ProjectBinding --- Reqnroll.VisualStudio/Discovery/ProjectBinding.cs | 5 ++++- Reqnroll.VisualStudio/Discovery/ProjectHookBinding.cs | 6 +----- .../Discovery/ProjectStepDefinitionBinding.cs | 6 ++---- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/Reqnroll.VisualStudio/Discovery/ProjectBinding.cs b/Reqnroll.VisualStudio/Discovery/ProjectBinding.cs index d7135726..c95c59e1 100644 --- a/Reqnroll.VisualStudio/Discovery/ProjectBinding.cs +++ b/Reqnroll.VisualStudio/Discovery/ProjectBinding.cs @@ -5,11 +5,14 @@ public class ProjectBinding { public Scope Scope { get; } public ProjectBindingImplementation Implementation { get; } + public virtual bool IsValid => Error == null && Scope?.IsValid != false; + public string Error { get; } - public ProjectBinding(ProjectBindingImplementation implementation, Scope scope) + public ProjectBinding(ProjectBindingImplementation implementation, Scope scope, string error = null) { Implementation = implementation; Scope = scope; + Error = error; } protected bool MatchScope(IGherkinDocumentContext context) diff --git a/Reqnroll.VisualStudio/Discovery/ProjectHookBinding.cs b/Reqnroll.VisualStudio/Discovery/ProjectHookBinding.cs index a33886c1..7ca43b0c 100644 --- a/Reqnroll.VisualStudio/Discovery/ProjectHookBinding.cs +++ b/Reqnroll.VisualStudio/Discovery/ProjectHookBinding.cs @@ -8,15 +8,11 @@ public class ProjectHookBinding : ProjectBinding public HookType HookType { get; } public int HookOrder { get; } - public bool IsValid => Error == null && Scope?.IsValid != false; - public string Error { get; } - public ProjectHookBinding(ProjectBindingImplementation implementation, Scope scope, HookType hookType, int? hookOrder, string error) - : base(implementation, scope) + : base(implementation, scope, error) { HookType = hookType; HookOrder = hookOrder ?? DefaultHookOrder; - Error = error; } public bool Match(Scenario scenario, IGherkinDocumentContext context) diff --git a/Reqnroll.VisualStudio/Discovery/ProjectStepDefinitionBinding.cs b/Reqnroll.VisualStudio/Discovery/ProjectStepDefinitionBinding.cs index 74482e52..bb0e0621 100644 --- a/Reqnroll.VisualStudio/Discovery/ProjectStepDefinitionBinding.cs +++ b/Reqnroll.VisualStudio/Discovery/ProjectStepDefinitionBinding.cs @@ -6,16 +6,14 @@ public class ProjectStepDefinitionBinding : ProjectBinding { public ProjectStepDefinitionBinding(ScenarioBlock stepDefinitionType, Regex regex, Scope scope, ProjectBindingImplementation implementation, string specifiedExpression = null, string error = null) - : base(implementation, scope) + : base(implementation, scope, error) { StepDefinitionType = stepDefinitionType; Regex = regex; SpecifiedExpression = specifiedExpression; - Error = error; } - public bool IsValid => Regex != null && Error == null && Scope?.IsValid != false; - public string Error { get; } + public override bool IsValid => Regex != null && base.IsValid; public ScenarioBlock StepDefinitionType { get; } public string SpecifiedExpression { get; } public Regex Regex { get; } From a397c8905a3fb7872eac3a36cebaa4b557c2e7f5 Mon Sep 17 00:00:00 2001 From: Chris Rudolphi <1702962+clrudolphi@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:03:40 -0600 Subject: [PATCH 4/6] Replaced built-in TagExpressionParser with ReqnrollTagExpressionParser (and Cucumber.TagExpression). --- .../Discovery/BindingImporter.cs | 39 ++- Reqnroll.VisualStudio/Discovery/Scope.cs | 2 +- .../IReqnrollTagExpressionParser.cs | 16 + .../TagExpressions/ITagExpression.cs | 6 - .../TagExpressions/InvalidTagExpression.cs | 20 ++ .../TagExpressions/ReqnrollTagExpression.cs | 25 ++ .../ReqnrollTagExpressionParser.cs | 66 +++++ .../TagExpressions/TagExpressionException.cs | 14 - .../TagExpressions/TagExpressionExtensions.cs | 1 + .../TagExpressions/TagExpressionParser.cs | 278 ------------------ .../Reqnroll.VisualStudio.csproj | 1 + .../ProjectBindingRegistryAmbiguousTests.cs | 2 +- .../ProjectBindingRegistryTestsBase.cs | 2 +- 13 files changed, 151 insertions(+), 321 deletions(-) create mode 100644 Reqnroll.VisualStudio/Discovery/TagExpressions/IReqnrollTagExpressionParser.cs delete mode 100644 Reqnroll.VisualStudio/Discovery/TagExpressions/ITagExpression.cs create mode 100644 Reqnroll.VisualStudio/Discovery/TagExpressions/InvalidTagExpression.cs create mode 100644 Reqnroll.VisualStudio/Discovery/TagExpressions/ReqnrollTagExpression.cs create mode 100644 Reqnroll.VisualStudio/Discovery/TagExpressions/ReqnrollTagExpressionParser.cs delete mode 100644 Reqnroll.VisualStudio/Discovery/TagExpressions/TagExpressionException.cs delete mode 100644 Reqnroll.VisualStudio/Discovery/TagExpressions/TagExpressionParser.cs diff --git a/Reqnroll.VisualStudio/Discovery/BindingImporter.cs b/Reqnroll.VisualStudio/Discovery/BindingImporter.cs index 130a3368..c716f793 100644 --- a/Reqnroll.VisualStudio/Discovery/BindingImporter.cs +++ b/Reqnroll.VisualStudio/Discovery/BindingImporter.cs @@ -7,15 +7,15 @@ namespace Reqnroll.VisualStudio.Discovery; public class BindingImporter { private static readonly string[] EmptyParameterTypes = new string[0]; - private static readonly string[] SingleStringParameterTypes = {TypeShortcuts.StringType}; - private static readonly string[] DoubleStringParameterTypes = {TypeShortcuts.StringType, TypeShortcuts.StringType}; - private static readonly string[] SingleIntParameterTypes = {TypeShortcuts.Int32Type}; - private static readonly string[] SingleDataTableParameterTypes = {TypeShortcuts.ReqnrollTableType}; + private static readonly string[] SingleStringParameterTypes = { TypeShortcuts.StringType }; + private static readonly string[] DoubleStringParameterTypes = { TypeShortcuts.StringType, TypeShortcuts.StringType }; + private static readonly string[] SingleIntParameterTypes = { TypeShortcuts.Int32Type }; + private static readonly string[] SingleDataTableParameterTypes = { TypeShortcuts.ReqnrollTableType }; private readonly Dictionary _implementations = new(); private readonly IDeveroomLogger _logger; private readonly Dictionary _sourceFiles; - private readonly TagExpressionParser _tagExpressionParser = new(); + private readonly ReqnrollTagExpressionParser _tagExpressionParser = new(); private readonly Dictionary _typeNames; public BindingImporter(Dictionary sourceFiles, Dictionary typeNames, @@ -158,28 +158,27 @@ private Scope ParseScope(StepScope bindingScope) if (bindingScope == null) return null; - try - { - return new Scope - { - FeatureTitle = bindingScope.FeatureTitle, - ScenarioTitle = bindingScope.ScenarioTitle, - Tag = string.IsNullOrWhiteSpace(bindingScope.Tag) - ? null - : _tagExpressionParser.Parse(bindingScope.Tag), - Error = bindingScope.Error - }; - } - catch (Exception ex) + var tagExpression = _tagExpressionParser.Parse(bindingScope.Tag); + + if (tagExpression is InvalidTagExpression ite) { - _logger.LogVerbose($"Invalid tag expression '{bindingScope.Tag}': {ex.Message}"); + _logger.LogVerbose($"Invalid tag expression '{bindingScope.Tag}': {ite.Message}"); return new Scope { FeatureTitle = bindingScope.FeatureTitle, ScenarioTitle = bindingScope.ScenarioTitle, Tag = null, - Error = $"Invalid tag expression '{bindingScope.Tag}': {ex.Message}" + Error = $"Invalid tag expression '{bindingScope.Tag}': {ite.Message}" }; } + return new Scope + { + FeatureTitle = bindingScope.FeatureTitle, + ScenarioTitle = bindingScope.ScenarioTitle, + Tag = string.IsNullOrWhiteSpace(bindingScope.Tag) + ? null + : tagExpression, + Error = bindingScope.Error + }; } } diff --git a/Reqnroll.VisualStudio/Discovery/Scope.cs b/Reqnroll.VisualStudio/Discovery/Scope.cs index 5dfd8416..5e194a6a 100644 --- a/Reqnroll.VisualStudio/Discovery/Scope.cs +++ b/Reqnroll.VisualStudio/Discovery/Scope.cs @@ -1,4 +1,4 @@ -using Reqnroll.VisualStudio.Discovery.TagExpressions; +using Cucumber.TagExpressions; namespace Reqnroll.VisualStudio.Discovery; diff --git a/Reqnroll.VisualStudio/Discovery/TagExpressions/IReqnrollTagExpressionParser.cs b/Reqnroll.VisualStudio/Discovery/TagExpressions/IReqnrollTagExpressionParser.cs new file mode 100644 index 00000000..ef37478c --- /dev/null +++ b/Reqnroll.VisualStudio/Discovery/TagExpressions/IReqnrollTagExpressionParser.cs @@ -0,0 +1,16 @@ +using Cucumber.TagExpressions; + +namespace Reqnroll.VisualStudio.Discovery.TagExpressions; + +///

+/// Defines a parser for tag expressions. +/// +public interface IReqnrollTagExpressionParser +{ + /// + /// Parses the specified text into an . + /// + /// The tag expression string to parse. + /// An representing the parsed expression. + ITagExpression Parse(string text); +} \ No newline at end of file diff --git a/Reqnroll.VisualStudio/Discovery/TagExpressions/ITagExpression.cs b/Reqnroll.VisualStudio/Discovery/TagExpressions/ITagExpression.cs deleted file mode 100644 index 24485c9b..00000000 --- a/Reqnroll.VisualStudio/Discovery/TagExpressions/ITagExpression.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Reqnroll.VisualStudio.Discovery.TagExpressions; - -public interface ITagExpression -{ - bool Evaluate(IEnumerable variables); -} diff --git a/Reqnroll.VisualStudio/Discovery/TagExpressions/InvalidTagExpression.cs b/Reqnroll.VisualStudio/Discovery/TagExpressions/InvalidTagExpression.cs new file mode 100644 index 00000000..b2aa7deb --- /dev/null +++ b/Reqnroll.VisualStudio/Discovery/TagExpressions/InvalidTagExpression.cs @@ -0,0 +1,20 @@ +using Cucumber.TagExpressions; +using System; + +namespace Reqnroll.VisualStudio.Discovery.TagExpressions; +public class InvalidTagExpression : ReqnrollTagExpression, ITagExpression +{ + public string Message { get; } + public InvalidTagExpression(ITagExpression expression, string originalTagExpression, string message) : base(expression, originalTagExpression) + { + Message = message; + } + public override bool Evaluate(System.Collections.Generic.IEnumerable tags) + { + throw new InvalidOperationException("Cannot evaluate an invalid tag expression: " + Message); + } + public override string ToString() + { + return "Invalid Tag Expression: " + Message; + } +} diff --git a/Reqnroll.VisualStudio/Discovery/TagExpressions/ReqnrollTagExpression.cs b/Reqnroll.VisualStudio/Discovery/TagExpressions/ReqnrollTagExpression.cs new file mode 100644 index 00000000..c97c757e --- /dev/null +++ b/Reqnroll.VisualStudio/Discovery/TagExpressions/ReqnrollTagExpression.cs @@ -0,0 +1,25 @@ +using Cucumber.TagExpressions; + +namespace Reqnroll.VisualStudio.Discovery.TagExpressions; + +public class ReqnrollTagExpression : ITagExpression +{ + public ReqnrollTagExpression(ITagExpression inner, string tagExpressionText) + { + TagExpressionText = tagExpressionText; + _inner = inner; + } + public string TagExpressionText { get; } + + private ITagExpression _inner; + + public override string ToString() + { + return _inner.ToString(); + } + + public virtual bool Evaluate(IEnumerable inputs) + { + return _inner.Evaluate(inputs); + } +} diff --git a/Reqnroll.VisualStudio/Discovery/TagExpressions/ReqnrollTagExpressionParser.cs b/Reqnroll.VisualStudio/Discovery/TagExpressions/ReqnrollTagExpressionParser.cs new file mode 100644 index 00000000..3aa57da3 --- /dev/null +++ b/Reqnroll.VisualStudio/Discovery/TagExpressions/ReqnrollTagExpressionParser.cs @@ -0,0 +1,66 @@ +using Cucumber.TagExpressions; + +namespace Reqnroll.VisualStudio.Discovery.TagExpressions; + +public class ReqnrollTagExpressionParser : IReqnrollTagExpressionParser +{ + public static ITagExpression CreateTagLiteral(string tag) => new LiteralNode(tag); + + public ITagExpression Parse(string tagExpression) + { + var tagExpressionParser = new TagExpressionParser(); + ITagExpression result = null; + try { + result = tagExpressionParser.Parse(tagExpression); + result = Rewrite(result); + return new ReqnrollTagExpression(result, tagExpression); + } + catch (TagExpressionException ex) + { + var msg = ex.Message; + if (ex.TagToken != null) + { + msg += $" (at offset {ex.TagToken.Position})"; + } + return new InvalidTagExpression(null, tagExpression, msg); + } + } + + // iff the expression is a literal node, prefix it with '@' if not already present + private ITagExpression Rewrite(ITagExpression expression) + { + if (expression is LiteralNode) + { + return PrefixLiteralNode(expression); + } + if (ConfirmExpressionHasAtPrefixes(expression)) + return expression; + throw new TagExpressionException("In multi-term tag expressions, all tag names must start with '@'."); + } + + private bool ConfirmExpressionHasAtPrefixes(ITagExpression expression) + { + switch (expression) + { + case NullExpression: + return true; + case BinaryOpNode binaryNode: + return ConfirmExpressionHasAtPrefixes(binaryNode.Left) && ConfirmExpressionHasAtPrefixes(binaryNode.Right); + case NotNode notNode: + return ConfirmExpressionHasAtPrefixes(notNode.Operand); + case LiteralNode literalNode: + return literalNode.Name.StartsWith("@"); + default: + throw new InvalidOperationException($"Unknown tag expression node type: {expression.GetType().FullName}"); + } + } + + private ITagExpression PrefixLiteralNode(ITagExpression expression) + { + var literalNode = (LiteralNode)expression; + if (string.IsNullOrEmpty(literalNode.Name) || literalNode.Name.StartsWith("@")) + return literalNode; + + return new LiteralNode("@" + literalNode.Name); + } +} diff --git a/Reqnroll.VisualStudio/Discovery/TagExpressions/TagExpressionException.cs b/Reqnroll.VisualStudio/Discovery/TagExpressions/TagExpressionException.cs deleted file mode 100644 index bcab811f..00000000 --- a/Reqnroll.VisualStudio/Discovery/TagExpressions/TagExpressionException.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Reqnroll.VisualStudio.Discovery.TagExpressions; - -public class TagExpressionException : Exception -{ - public TagExpressionException(string message) : base(message) - { - } - - public TagExpressionException(string message, Exception inner) : base(message, inner) - { - } -} diff --git a/Reqnroll.VisualStudio/Discovery/TagExpressions/TagExpressionExtensions.cs b/Reqnroll.VisualStudio/Discovery/TagExpressions/TagExpressionExtensions.cs index 58890d05..0cc42eb1 100644 --- a/Reqnroll.VisualStudio/Discovery/TagExpressions/TagExpressionExtensions.cs +++ b/Reqnroll.VisualStudio/Discovery/TagExpressions/TagExpressionExtensions.cs @@ -1,3 +1,4 @@ +using Cucumber.TagExpressions; using System.Linq; namespace Reqnroll.VisualStudio.Discovery.TagExpressions; diff --git a/Reqnroll.VisualStudio/Discovery/TagExpressions/TagExpressionParser.cs b/Reqnroll.VisualStudio/Discovery/TagExpressions/TagExpressionParser.cs deleted file mode 100644 index e4fac94a..00000000 --- a/Reqnroll.VisualStudio/Discovery/TagExpressions/TagExpressionParser.cs +++ /dev/null @@ -1,278 +0,0 @@ -#nullable disable - -namespace Reqnroll.VisualStudio.Discovery.TagExpressions; - -public class TagExpressionParser -{ - private const char ESCAPING_CHAR = '\\'; - - private static readonly IDictionary ASSOC = new Dictionary - { - {"or", Assoc.LEFT}, - {"and", Assoc.LEFT}, - {"not", Assoc.RIGHT} - }; - - private static readonly IDictionary PREC = new Dictionary - { - {"(", -2}, - {")", -1}, - {"or", 0}, - {"and", 1}, - {"not", 2} - }; - - public virtual ITagExpression Parse(string infix) - { - var tokens = Tokenize(infix); - if (!tokens.Any()) return new True(); - - var operators = new Stack(); - var expressions = new Stack(); - TokenType expectedTokenType = TokenType.OPERAND; - foreach (string token in tokens) - if (IsUnary(token)) - { - Check(expectedTokenType, TokenType.OPERAND); - operators.Push(token); - expectedTokenType = TokenType.OPERAND; - } - else if (IsBinary(token)) - { - Check(expectedTokenType, TokenType.OPERATOR); - while (operators.Count > 0 && IsOperator(operators.Peek()) && ( - ASSOC[token] == Assoc.LEFT && PREC[token] <= PREC[operators.Peek()] - || - ASSOC[token] == Assoc.RIGHT && PREC[token] < PREC[operators.Peek()]) - ) - PushExpr(Pop(operators), expressions); - operators.Push(token); - expectedTokenType = TokenType.OPERAND; - } - else if ("(".Equals(token)) - { - Check(expectedTokenType, TokenType.OPERAND); - operators.Push(token); - expectedTokenType = TokenType.OPERAND; - } - else if (")".Equals(token)) - { - Check(expectedTokenType, TokenType.OPERATOR); - while (operators.Count > 0 && !"(".Equals(operators.Peek())) PushExpr(Pop(operators), expressions); - if (operators.Count == 0) throw new TagExpressionException("Syntax error. Unmatched )"); - if ("(".Equals(operators.Peek())) Pop(operators); - expectedTokenType = TokenType.OPERATOR; - } - else - { - Check(expectedTokenType, TokenType.OPERAND); - PushExpr(token, expressions); - expectedTokenType = TokenType.OPERATOR; - } - - while (operators.Count > 0) - { - if ("(".Equals(operators.Peek())) throw new TagExpressionException("Syntax error. Unmatched ("); - PushExpr(Pop(operators), expressions); - } - - return expressions.Pop(); - } - - private static List Tokenize(string expr) - { - var tokens = new List(); - - bool isEscaped = false; - StringBuilder token = null; - for (int i = 0; i < expr.Length; i++) - { - char c = expr[i]; - if (ESCAPING_CHAR == c) - { - isEscaped = true; - } - else - { - if (char.IsWhiteSpace(c)) - { - // skip - if (null != token) - { - // end of token - tokens.Add(token.ToString()); - token = null; - } - } - else - { - switch (c) - { - case '(': - case ')': - if (!isEscaped) - { - if (null != token) - { - // end of token - tokens.Add(token.ToString()); - token = null; - } - - tokens.Add(c.ToString()); - } - - break; - default: - if (null == token) - // start of token - token = new StringBuilder(); - token.Append(c); - break; - } - } - - isEscaped = false; - } - } - - if (null != token) - // end of token - tokens.Add(token.ToString()); - return tokens; - } - - private void Check(TokenType expectedTokenType, TokenType tokenType) - { - if (expectedTokenType != tokenType) - throw new TagExpressionException( - $"Syntax error. Expected {expectedTokenType.ToString().ToLowerInvariant()}"); - } - - private T Pop(Stack stack) - { - if (!stack.Any()) throw new TagExpressionException("empty stack"); - return stack.Pop(); - } - - private void PushExpr(string token, Stack stack) - { - switch (token) - { - case "and": - ITagExpression rightAndExpr = Pop(stack); - stack.Push(new And(Pop(stack), rightAndExpr)); - break; - case "or": - ITagExpression rightOrExpr = Pop(stack); - stack.Push(new Or(Pop(stack), rightOrExpr)); - break; - case "not": - stack.Push(new Not(Pop(stack))); - break; - default: - stack.Push(new Literal(token)); - break; - } - } - - private bool IsUnary(string token) => "not".Equals(token); - - private bool IsBinary(string token) => "or".Equals(token) || "and".Equals(token); - - private bool IsOperator(string token) => ASSOC.ContainsKey(token); - - public static ITagExpression CreateTagLiteral(string tag) => new Literal(tag); - - private static string[] EnsureArray(IEnumerable variables) - { - if (variables is string[] variablesArray) - return variablesArray; - return variables.ToArray(); - } - - private enum TokenType - { - OPERAND, - OPERATOR - } - - private enum Assoc - { - LEFT, - RIGHT - } - - private class Literal : ITagExpression - { - private readonly string value; - - public Literal(string value) - { - this.value = value; - } - - public bool Evaluate(IEnumerable variables) => variables.Contains(value); - - public override string ToString() => value.Replace("\\(", "\\\\(").Replace("\\)", "\\\\)"); - } - - private class Or : ITagExpression - { - private readonly ITagExpression left; - private readonly ITagExpression right; - - public Or(ITagExpression left, ITagExpression right) - { - this.left = left; - this.right = right; - } - - public bool Evaluate(IEnumerable variables) - { - var variablesArray = EnsureArray(variables); - return left.Evaluate(variablesArray) || right.Evaluate(variablesArray); - } - - public override string ToString() => "( " + left + " or " + right + " )"; - } - - private class And : ITagExpression - { - private readonly ITagExpression left; - private readonly ITagExpression right; - - public And(ITagExpression left, ITagExpression right) - { - this.left = left; - this.right = right; - } - - public bool Evaluate(IEnumerable variables) - { - var variablesArray = EnsureArray(variables); - return left.Evaluate(variablesArray) && right.Evaluate(variablesArray); - } - - public override string ToString() => "( " + left + " and " + right + " )"; - } - - private class Not : ITagExpression - { - private readonly ITagExpression expr; - - public Not(ITagExpression expr) - { - this.expr = expr; - } - - public bool Evaluate(IEnumerable variables) => !expr.Evaluate(variables); - - public override string ToString() => "not ( " + expr + " )"; - } - - private class True : ITagExpression - { - public bool Evaluate(IEnumerable variables) => true; - } -} diff --git a/Reqnroll.VisualStudio/Reqnroll.VisualStudio.csproj b/Reqnroll.VisualStudio/Reqnroll.VisualStudio.csproj index d7ff0e86..a2d3bd67 100644 --- a/Reqnroll.VisualStudio/Reqnroll.VisualStudio.csproj +++ b/Reqnroll.VisualStudio/Reqnroll.VisualStudio.csproj @@ -13,6 +13,7 @@ + diff --git a/Tests/Reqnroll.VisualStudio.Tests/Discovery/ProjectBindingRegistryAmbiguousTests.cs b/Tests/Reqnroll.VisualStudio.Tests/Discovery/ProjectBindingRegistryAmbiguousTests.cs index 81ae58f3..173bc035 100644 --- a/Tests/Reqnroll.VisualStudio.Tests/Discovery/ProjectBindingRegistryAmbiguousTests.cs +++ b/Tests/Reqnroll.VisualStudio.Tests/Discovery/ProjectBindingRegistryAmbiguousTests.cs @@ -94,7 +94,7 @@ public void Matches_disambiguates_single_stepDef_with_multiple_matching_Scopes() private ProjectStepDefinitionBinding CreateStepDefinitionBindingWithScope(string stepRegex, string scopeText, string methodName) { - var scope = new Scope() { Tag = new TagExpressionParser().Parse(scopeText) }; + var scope = new Scope() { Tag = new ReqnrollTagExpressionParser().Parse(scopeText) }; return CreateStepDefinitionBinding(stepRegex, ScenarioBlock.Given, scope, null, methodName); } } diff --git a/Tests/Reqnroll.VisualStudio.Tests/Discovery/ProjectBindingRegistryTestsBase.cs b/Tests/Reqnroll.VisualStudio.Tests/Discovery/ProjectBindingRegistryTestsBase.cs index 18789e39..54b6dff7 100644 --- a/Tests/Reqnroll.VisualStudio.Tests/Discovery/ProjectBindingRegistryTestsBase.cs +++ b/Tests/Reqnroll.VisualStudio.Tests/Discovery/ProjectBindingRegistryTestsBase.cs @@ -45,7 +45,7 @@ protected static DataTable CreateDataTable() }); } - protected Scope CreateTagScope(string tagName) => new() {Tag = TagExpressionParser.CreateTagLiteral(tagName)}; + protected Scope CreateTagScope(string tagName) => new() {Tag = ReqnrollTagExpressionParser.CreateTagLiteral(tagName)}; private DeveroomTag CreateFeatureStructure(string[] featureTags, string[] scenarioTags, string[] scenarioOutlineTags = null, string[] soHeaders = null, string[][] soCells = null, From fa21e5ca03eab18da2b556325b033392cfb5aee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1sp=C3=A1r=20Nagy?= Date: Fri, 16 Jan 2026 14:25:21 +0100 Subject: [PATCH 5/6] Added dependency to the package --- .../Reqnroll.VisualStudio.Package.csproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Reqnroll.VisualStudio.Package/Reqnroll.VisualStudio.Package.csproj b/Reqnroll.VisualStudio.Package/Reqnroll.VisualStudio.Package.csproj index 3b6ae380..8c6aea40 100644 --- a/Reqnroll.VisualStudio.Package/Reqnroll.VisualStudio.Package.csproj +++ b/Reqnroll.VisualStudio.Package/Reqnroll.VisualStudio.Package.csproj @@ -91,10 +91,11 @@ + - - + + From fdbc1d688b6e7aca6dc0c0a9be1d1128220dbfa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1sp=C3=A1r=20Nagy?= Date: Fri, 16 Jan 2026 14:26:57 +0100 Subject: [PATCH 6/6] update CHANGELOG --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb0c0152..c2ae69fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,11 @@ ## Improvements: +* Improved handling of scope tag expressions, hook and scope errors (#150) + ## Bug fixes: -*Contributors of this release (in alphabetical order):* +*Contributors of this release (in alphabetical order):* @clrudolphi, @gasparnagy # v2025.3.395 - 2025-12-17