From 3c5c3ecac2565e11506f163b446147f1c8db2e15 Mon Sep 17 00:00:00 2001 From: yusing Date: Tue, 24 Feb 2026 01:42:40 +0800 Subject: [PATCH] fix(rules): handle empty matcher as unconditional rule The matcherSignature function now treats empty strings as unconditional rules that match any request, returning "(any)" as the signature instead of rejecting them. This enables proper dead code detection when an unconditional terminating rule shadows later rules. Adds test coverage for detecting dead rules caused by unconditional terminating rules. --- internal/route/rules/rules.go | 2 +- internal/route/rules/rules_test.go | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/internal/route/rules/rules.go b/internal/route/rules/rules.go index f8b7e93e..c32d439d 100644 --- a/internal/route/rules/rules.go +++ b/internal/route/rules/rules.go @@ -155,7 +155,7 @@ func ruleOnAlwaysTrue(on RuleOn) bool { func matcherSignature(raw string) (string, bool) { raw = strings.TrimSpace(raw) if raw == "" { - return "", false + return "(any)", true // unconditional rule } andParts := splitAnd(raw) diff --git a/internal/route/rules/rules_test.go b/internal/route/rules/rules_test.go index b507a891..baa66b95 100644 --- a/internal/route/rules/rules_test.go +++ b/internal/route/rules/rules_test.go @@ -124,6 +124,19 @@ header Host example.com { `, want: nil, }, + { + name: "unconditional terminating rule shadows later unconditional rule", + rules: ` +{ + error 404 "not found" +} + +{ + error 403 "forbidden" +} +`, + want: ErrDeadRule, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {