@@ -32,16 +32,17 @@ interface ShellScanFrame {
3232 bracketDepth ?: number
3333 /** A bare name[index] is arithmetic only when its closing bracket is followed by assignment. */
3434 arrayAssignment ?: boolean
35+ arithmeticParent ?: ShellScanFrame
3536 literalRoot : boolean
3637}
3738
3839interface ShellOccurrenceContext {
3940 quote : ShellQuote
4041 /**
41- * Enclosing arithmetic also re-evaluates nested substitutions. Keep frame references so a
42+ * Enclosing arithmetic also re-evaluates nested substitutions. Keep a frame reference so a
4243 * bare subscript can be classified when its closing bracket confirms an assignment.
4344 */
44- arithmeticFrames ?: ShellScanFrame [ ]
45+ arithmeticFrame ?: ShellScanFrame
4546 unsupported ?: 'escaped sequence'
4647}
4748
@@ -78,6 +79,12 @@ function shellArithmeticCommandStarts(code: string, index: number): boolean {
7879 return previous === undefined || / \s | [ ; & | ( ) < > ] / . test ( previous )
7980}
8081
82+ function pushShellFrame ( frames : ShellScanFrame [ ] , frame : ShellScanFrame ) : void {
83+ const parent = frames . at ( - 1 )
84+ frame . arithmeticParent = parent ?. kind === 'arithmetic' ? parent : parent ?. arithmeticParent
85+ frames . push ( frame )
86+ }
87+
8188function readShellExpansionStart (
8289 code : string ,
8390 index : number ,
@@ -304,10 +311,10 @@ function parseHeredocHeaders(
304311 if ( character === '\\' ) index += 1
305312 else if ( character === '"' ) frame . quote = 'none'
306313 else if ( expansion ) {
307- frames . push ( expansion . frame )
314+ pushShellFrame ( frames , expansion . frame )
308315 index += expansion . length - 1
309316 } else if ( character === '`' ) {
310- frames . push ( {
317+ pushShellFrame ( frames , {
311318 kind : 'backtick' ,
312319 quote : 'none' ,
313320 parenthesisDepth : 0 ,
@@ -339,12 +346,12 @@ function parseHeredocHeaders(
339346 continue
340347 }
341348 if ( expansion ) {
342- frames . push ( expansion . frame )
349+ pushShellFrame ( frames , expansion . frame )
343350 index += expansion . length - 1
344351 continue
345352 }
346353 if ( character === '`' ) {
347- frames . push ( {
354+ pushShellFrame ( frames , {
348355 kind : 'backtick' ,
349356 quote : 'none' ,
350357 parenthesisDepth : 0 ,
@@ -483,8 +490,8 @@ function getUnsupportedShellPosition(
483490 occurrence : CodePlaceholderOccurrence ,
484491 context : ShellOccurrenceContext
485492) : string | undefined {
486- if ( context . arithmeticFrames ?. some ( ( frame ) => frame . arrayAssignment !== false ) ) {
487- return 'in a shell arithmetic expression'
493+ for ( let frame = context . arithmeticFrame ; frame ; frame = frame . arithmeticParent ) {
494+ if ( frame . arrayAssignment !== false ) return 'in a shell arithmetic expression'
488495 }
489496 if ( code [ occurrence . start - 1 ] === '$' ) return 'immediately after "$"'
490497 if ( context . quote !== 'none' ) return undefined
@@ -533,7 +540,7 @@ function collectShellOccurrenceContexts(
533540 if ( occurrence ) {
534541 contexts . set ( occurrence , {
535542 quote : frame . quote ,
536- arithmeticFrames : frames . filter ( ( candidate ) => candidate . kind === 'arithmetic' ) ,
543+ arithmeticFrame : frame . kind === 'arithmetic' ? frame : frame . arithmeticParent ,
537544 } )
538545 index = occurrence . end
539546 continue
@@ -567,7 +574,7 @@ function collectShellOccurrenceContexts(
567574 )
568575 const array = readShellArrayStart ( code , index , frame )
569576 if ( array ) {
570- frames . push ( array )
577+ pushShellFrame ( frames , array )
571578 index += 1
572579 continue
573580 }
@@ -584,10 +591,10 @@ function collectShellOccurrenceContexts(
584591 frame . quote = 'none'
585592 index += 1
586593 } else if ( expansion ) {
587- frames . push ( expansion . frame )
594+ pushShellFrame ( frames , expansion . frame )
588595 index += expansion . length
589596 } else if ( character === '`' ) {
590- frames . push ( {
597+ pushShellFrame ( frames , {
591598 kind : 'backtick' ,
592599 quote : 'none' ,
593600 parenthesisDepth : 0 ,
@@ -636,12 +643,12 @@ function collectShellOccurrenceContexts(
636643 continue
637644 }
638645 if ( expansion ) {
639- frames . push ( expansion . frame )
646+ pushShellFrame ( frames , expansion . frame )
640647 index += expansion . length
641648 continue
642649 }
643650 if ( character === '`' ) {
644- frames . push ( {
651+ pushShellFrame ( frames , {
645652 kind : 'backtick' ,
646653 quote : 'none' ,
647654 parenthesisDepth : 0 ,
0 commit comments