@@ -125,23 +125,32 @@ impl From<serde_json::Error> for ParseGrammarError {
125125 }
126126}
127127
128- fn rule_is_referenced ( rule : & Rule , target : & str ) -> bool {
128+ /// Check if a rule is referenced by another rule.
129+ ///
130+ /// This function is used to determine if a variable is used in a given rule,
131+ /// and `is_other` indicates if the rule is an external, and if it is,
132+ /// to not assume that a named symbol that is equal to itself means it's being referenced.
133+ ///
134+ /// For example, if we have an external rule **and** a normal rule both called `foo`,
135+ /// `foo` should not be thought of as directly used unless it's used within another rule.
136+ fn rule_is_referenced ( rule : & Rule , target : & str , is_external : bool ) -> bool {
129137 match rule {
130- Rule :: NamedSymbol ( name) => name == target,
138+ Rule :: NamedSymbol ( name) => name == target && !is_external ,
131139 Rule :: Choice ( rules) | Rule :: Seq ( rules) => {
132- rules. iter ( ) . any ( |r| rule_is_referenced ( r, target) )
140+ rules. iter ( ) . any ( |r| rule_is_referenced ( r, target, false ) )
133141 }
134142 Rule :: Metadata { rule, .. } | Rule :: Reserved { rule, .. } => {
135- rule_is_referenced ( rule, target)
143+ rule_is_referenced ( rule, target, is_external )
136144 }
137- Rule :: Repeat ( inner) => rule_is_referenced ( inner, target) ,
145+ Rule :: Repeat ( inner) => rule_is_referenced ( inner, target, false ) ,
138146 Rule :: Blank | Rule :: String ( _) | Rule :: Pattern ( _, _) | Rule :: Symbol ( _) => false ,
139147 }
140148}
141149
142150fn variable_is_used (
143151 grammar_rules : & [ ( String , Rule ) ] ,
144- other_rules : ( & [ Rule ] , & [ Rule ] ) ,
152+ extras : & [ Rule ] ,
153+ externals : & [ Rule ] ,
145154 target_name : & str ,
146155 in_progress : & mut HashSet < String > ,
147156) -> bool {
@@ -150,11 +159,16 @@ fn variable_is_used(
150159 return true ;
151160 }
152161
153- if other_rules
154- . 0
162+ if extras
155163 . iter ( )
156- . chain ( other_rules. 1 . iter ( ) )
157- . any ( |rule| rule_is_referenced ( rule, target_name) )
164+ . any ( |rule| rule_is_referenced ( rule, target_name, false ) )
165+ {
166+ return true ;
167+ }
168+
169+ if externals
170+ . iter ( )
171+ . any ( |rule| rule_is_referenced ( rule, target_name, true ) )
158172 {
159173 return true ;
160174 }
@@ -164,10 +178,10 @@ fn variable_is_used(
164178 . iter ( )
165179 . filter ( |( key, _) | * key != target_name)
166180 . any ( |( name, rule) | {
167- if !rule_is_referenced ( rule, target_name) || in_progress. contains ( name) {
181+ if !rule_is_referenced ( rule, target_name, false ) || in_progress. contains ( name) {
168182 return false ;
169183 }
170- variable_is_used ( grammar_rules, other_rules , name, in_progress)
184+ variable_is_used ( grammar_rules, extras , externals , name, in_progress)
171185 } ) ;
172186 in_progress. remove ( target_name) ;
173187
@@ -224,16 +238,17 @@ pub(crate) fn parse_grammar(input: &str) -> ParseGrammarResult<InputGrammar> {
224238 for ( name, rule) in & rules {
225239 if !variable_is_used (
226240 & rules,
227- ( & extra_symbols, & external_tokens) ,
241+ & extra_symbols,
242+ & external_tokens,
228243 name,
229244 & mut in_progress,
230- ) && grammar_json. word . as_ref ( ) . is_some_and ( |w| w != name)
245+ ) && grammar_json. word . as_ref ( ) . is_none_or ( |w| w != name)
231246 {
232247 grammar_json. conflicts . retain ( |r| !r. contains ( name) ) ;
233248 grammar_json. supertypes . retain ( |r| r != name) ;
234249 grammar_json. inline . retain ( |r| r != name) ;
235- extra_symbols. retain ( |r| !rule_is_referenced ( r, name) ) ;
236- external_tokens. retain ( |r| !rule_is_referenced ( r, name) ) ;
250+ extra_symbols. retain ( |r| !rule_is_referenced ( r, name, true ) ) ;
251+ external_tokens. retain ( |r| !rule_is_referenced ( r, name, true ) ) ;
237252 precedence_orderings. retain ( |r| {
238253 !r. iter ( ) . any ( |e| {
239254 let PrecedenceEntry :: Symbol ( s) = e else {
0 commit comments