Skip to content

Commit cf27384

Browse files
authored
query: case has effect in nested expressions (#1022)
Previously only the "top-level" "case:" affected the query. So if you had a "case" in any sub expression it would have no effect. This adjusts our implementation to track nested cases in our query parser and allow them to affect the final case sensitivity on query.Qs. Test Plan: added more test cases to demonstrate the problem. These failed before this commit.
1 parent 034d5a3 commit cf27384

2 files changed

Lines changed: 74 additions & 0 deletions

File tree

query/parse.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,43 @@ func (o *orOperator) String() string {
6868
return "orOp"
6969
}
7070

71+
// caseScopeQ is a parse-time wrapper used to prevent case directives from an
72+
// outer expression list from overriding an explicitly scoped inner `case:`.
73+
type caseScopeQ struct {
74+
Child Q
75+
}
76+
77+
func (c *caseScopeQ) String() string {
78+
return c.Child.String()
79+
}
80+
81+
func stripCaseScopesList(qs []Q) []Q {
82+
stripped := make([]Q, len(qs))
83+
for i, q := range qs {
84+
stripped[i] = stripCaseScopes(q)
85+
}
86+
return stripped
87+
}
88+
89+
func stripCaseScopes(q Q) Q {
90+
switch s := q.(type) {
91+
case *And:
92+
return &And{Children: stripCaseScopesList(s.Children)}
93+
case *Or:
94+
return &Or{Children: stripCaseScopesList(s.Children)}
95+
case *Not:
96+
return &Not{Child: stripCaseScopes(s.Child)}
97+
case *Type:
98+
return &Type{Type: s.Type, Child: stripCaseScopes(s.Child)}
99+
case *Boost:
100+
return &Boost{Boost: s.Boost, Child: stripCaseScopes(s.Child)}
101+
case *caseScopeQ:
102+
return stripCaseScopes(s.Child)
103+
default:
104+
return q
105+
}
106+
}
107+
71108
func isSpace(c byte) bool {
72109
return c == ' ' || c == '\t'
73110
}
@@ -90,6 +127,8 @@ func Parse(qStr string) (Q, error) {
90127
return nil, err
91128
}
92129

130+
q = stripCaseScopes(q)
131+
93132
return Simplify(q), nil
94133
}
95134

@@ -354,12 +393,14 @@ func parseExprList(in []byte) ([]Q, int, error) {
354393
}
355394

356395
setCase := "auto"
396+
hasCaseScope := false
357397
newQS := qs[:0]
358398
typeT := uint8(100)
359399
for _, q := range qs {
360400
switch s := q.(type) {
361401
case *caseQ:
362402
setCase = s.Flavor
403+
hasCaseScope = true
363404
case *Type:
364405
if s.Type < typeT {
365406
typeT = s.Type
@@ -377,6 +418,19 @@ func parseExprList(in []byte) ([]Q, int, error) {
377418
if typeT != 100 {
378419
qs = []Q{&Type{Type: typeT, Child: NewAnd(qs...)}}
379420
}
421+
422+
if hasCaseScope {
423+
scoped := make([]Q, 0, len(qs))
424+
for _, q := range qs {
425+
if _, isOrOperator := q.(*orOperator); isOrOperator {
426+
scoped = append(scoped, q)
427+
continue
428+
}
429+
scoped = append(scoped, &caseScopeQ{Child: q})
430+
}
431+
qs = scoped
432+
}
433+
380434
return qs, len(in) - len(b), nil
381435
}
382436

query/parse_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,26 @@ func TestParseQuery(t *testing.T) {
108108
&Substring{Pattern: "abc", CaseSensitive: true},
109109
&Not{Child: &Substring{Pattern: "def", FileName: true, CaseSensitive: true}},
110110
)},
111+
{"(foo case:yes) bar", NewAnd(
112+
&Substring{Pattern: "foo", CaseSensitive: true},
113+
&Substring{Pattern: "bar"},
114+
)},
115+
{"(case:yes foo) bar", NewAnd(
116+
&Substring{Pattern: "foo", CaseSensitive: true},
117+
&Substring{Pattern: "bar"},
118+
)},
119+
{"(case:yes foo (bar))", NewAnd(
120+
&Substring{Pattern: "foo", CaseSensitive: true},
121+
&Substring{Pattern: "bar", CaseSensitive: true},
122+
)},
123+
{"case:auto (foo case:yes) bar", NewAnd(
124+
&Substring{Pattern: "foo", CaseSensitive: true},
125+
&Substring{Pattern: "bar"},
126+
)},
127+
{"case:yes (foo case:no) bar", NewAnd(
128+
&Substring{Pattern: "foo"},
129+
&Substring{Pattern: "bar", CaseSensitive: true},
130+
)},
111131

112132
// type
113133
{"type:repo abc", &Type{Type: TypeRepo, Child: &Substring{Pattern: "abc"}}},

0 commit comments

Comments
 (0)