@@ -15,6 +15,14 @@ import type { RegExpContext } from "../utils"
15
15
import { createRule , defineRegexpVisitor } from "../utils"
16
16
import { mention } from "../utils/mention"
17
17
18
+ type MessageId =
19
+ | "nested"
20
+ | "disjunctive"
21
+ | "intoNegativeLookaround"
22
+ | "forward"
23
+ | "backward"
24
+ | "empty"
25
+
18
26
/**
19
27
* Returns whether the list of ancestors from `from` to `to` contains a negated
20
28
* lookaround.
@@ -35,16 +43,67 @@ function hasNegatedLookaroundInBetween(
35
43
return false
36
44
}
37
45
46
+ /**
47
+ * Returns the problem information specifying the reason why the backreference is
48
+ * useless.
49
+ */
50
+ function getUselessProblem (
51
+ backRef : Backreference ,
52
+ flags : ReadonlyFlags ,
53
+ ) : { messageId : MessageId ; group : CapturingGroup ; otherGroups : string } | null {
54
+ const groups = [ backRef . resolved ] . flat ( )
55
+
56
+ const problems : { messageId : MessageId ; group : CapturingGroup } [ ] = [ ]
57
+ for ( const group of groups ) {
58
+ const messageId = getUselessMessageId ( backRef , group , flags )
59
+ if ( ! messageId ) {
60
+ return null
61
+ }
62
+ problems . push ( { messageId, group } )
63
+ }
64
+ if ( problems . length === 0 ) {
65
+ return null
66
+ }
67
+
68
+ let problemsToReport
69
+
70
+ // Gets problems that appear in the same disjunction.
71
+ const problemsInSameDisjunction = problems . filter (
72
+ ( problem ) => problem . messageId !== "disjunctive" ,
73
+ )
74
+
75
+ if ( problemsInSameDisjunction . length ) {
76
+ // Only report problems that appear in the same disjunction.
77
+ problemsToReport = problemsInSameDisjunction
78
+ } else {
79
+ // If all groups appear in different disjunctions, report it.
80
+ problemsToReport = problems
81
+ }
82
+
83
+ const [ { messageId, group } , ...other ] = problemsToReport
84
+ let otherGroups = ""
85
+
86
+ if ( other . length === 1 ) {
87
+ otherGroups = " and another group"
88
+ } else if ( other . length > 1 ) {
89
+ otherGroups = ` and other ${ other . length } groups`
90
+ }
91
+ return {
92
+ messageId,
93
+ group,
94
+ otherGroups,
95
+ }
96
+ }
97
+
38
98
/**
39
99
* Returns the message id specifying the reason why the backreference is
40
100
* useless.
41
101
*/
42
102
function getUselessMessageId (
43
103
backRef : Backreference ,
104
+ group : CapturingGroup ,
44
105
flags : ReadonlyFlags ,
45
- ) : string | null {
46
- const group = backRef . resolved
47
-
106
+ ) : MessageId | null {
48
107
const closestAncestor = getClosestAncestor ( backRef , group )
49
108
50
109
if ( closestAncestor === group ) {
@@ -93,16 +152,16 @@ export default createRule("no-useless-backreference", {
93
152
} ,
94
153
schema : [ ] ,
95
154
messages : {
96
- nested : "Backreference {{ bref }} will be ignored. It references group {{ group }} from within that group." ,
155
+ nested : "Backreference {{ bref }} will be ignored. It references group {{ group }}{{ otherGroups }} from within that group." ,
97
156
forward :
98
- "Backreference {{ bref }} will be ignored. It references group {{ group }} which appears later in the pattern." ,
157
+ "Backreference {{ bref }} will be ignored. It references group {{ group }}{{ otherGroups }} which appears later in the pattern." ,
99
158
backward :
100
- "Backreference {{ bref }} will be ignored. It references group {{ group }} which appears before in the same lookbehind." ,
159
+ "Backreference {{ bref }} will be ignored. It references group {{ group }}{{ otherGroups }} which appears before in the same lookbehind." ,
101
160
disjunctive :
102
- "Backreference {{ bref }} will be ignored. It references group {{ group }} which is in another alternative." ,
161
+ "Backreference {{ bref }} will be ignored. It references group {{ group }}{{ otherGroups }} which is in another alternative." ,
103
162
intoNegativeLookaround :
104
- "Backreference {{ bref }} will be ignored. It references group {{ group }} which is in a negative lookaround." ,
105
- empty : "Backreference {{ bref }} will be ignored. It references group {{ group }} which always captures zero characters." ,
163
+ "Backreference {{ bref }} will be ignored. It references group {{ group }}{{ otherGroups }} which is in a negative lookaround." ,
164
+ empty : "Backreference {{ bref }} will be ignored. It references group {{ group }}{{ otherGroups }} which always captures zero characters." ,
106
165
} ,
107
166
type : "suggestion" , // "problem",
108
167
} ,
@@ -114,16 +173,17 @@ export default createRule("no-useless-backreference", {
114
173
} : RegExpContext ) : RegExpVisitor . Handlers {
115
174
return {
116
175
onBackreferenceEnter ( backRef ) {
117
- const messageId = getUselessMessageId ( backRef , flags )
176
+ const problem = getUselessProblem ( backRef , flags )
118
177
119
- if ( messageId ) {
178
+ if ( problem ) {
120
179
context . report ( {
121
180
node,
122
181
loc : getRegexpLocation ( backRef ) ,
123
- messageId,
182
+ messageId : problem . messageId ,
124
183
data : {
125
184
bref : mention ( backRef ) ,
126
- group : mention ( backRef . resolved ) ,
185
+ group : mention ( problem . group ) ,
186
+ otherGroups : problem . otherGroups ,
127
187
} ,
128
188
} )
129
189
}
0 commit comments