@@ -418,11 +418,24 @@ function bashunit::coverage::aggregate_parallel() {
418418 fi
419419}
420420
421- # Pre-compiled regex pattern for function declarations (performance optimization)
422- # Matches: function foo() { OR foo() { OR function foo() OR foo()
423- # Does NOT match single-line functions with body: function foo() { echo "hi"; }
424- _BASHUNIT_COVERAGE_FUNC_PATTERN=' ^[[:space:]]*(function[[:space:]]+)?'
425- _BASHUNIT_COVERAGE_FUNC_PATTERN=" ${_BASHUNIT_COVERAGE_FUNC_PATTERN} " ' [a-zA-Z_][a-zA-Z0-9_:]*[[:space:]]*\(\)[[:space:]]*\{?[[:space:]]*$'
421+ # Pre-compiled combined regex of all non-executable line patterns.
422+ # Collapses multiple grep subshells into a single invocation per line for performance.
423+ # Each alternation is fully self-anchored so semantics match the original per-pattern checks.
424+ # Patterns covered (in order):
425+ # - comment-only lines (including shebang)
426+ # - function declarations (but not single-line functions with a body)
427+ # - brace-only lines
428+ # - control flow keywords (then, else, fi, do, done, esac, in, ;;, ;;&, ;&)
429+ # - loop terminators with redirection/pipe/fd (e.g. "done < file", "done | sort")
430+ # - case patterns like "--option)" or "*) # comment"
431+ # - standalone ) for arrays/subshells
432+ _BASHUNIT_COVERAGE_NONEXEC_PATTERN=' ^[[:space:]]*#'
433+ _BASHUNIT_COVERAGE_NONEXEC_PATTERN=" ${_BASHUNIT_COVERAGE_NONEXEC_PATTERN} " ' |^[[:space:]]*(function[[:space:]]+)?[a-zA-Z_][a-zA-Z0-9_:]*[[:space:]]*\(\)[[:space:]]*\{?[[:space:]]*$'
434+ _BASHUNIT_COVERAGE_NONEXEC_PATTERN=" ${_BASHUNIT_COVERAGE_NONEXEC_PATTERN} " ' |^[[:space:]]*[\{\}][[:space:]]*$'
435+ _BASHUNIT_COVERAGE_NONEXEC_PATTERN=" ${_BASHUNIT_COVERAGE_NONEXEC_PATTERN} " ' |^[[:space:]]*(then|else|fi|do|done|esac|in|;;|;;&|;&)[[:space:]]*(#.*)?$'
436+ _BASHUNIT_COVERAGE_NONEXEC_PATTERN=" ${_BASHUNIT_COVERAGE_NONEXEC_PATTERN} " ' |^[[:space:]]*done[[:space:]]+[^[:space:]#].*$'
437+ _BASHUNIT_COVERAGE_NONEXEC_PATTERN=" ${_BASHUNIT_COVERAGE_NONEXEC_PATTERN} " ' |^[[:space:]]*[^\)]+\)[[:space:]]*(#.*)?$'
438+ _BASHUNIT_COVERAGE_NONEXEC_PATTERN=" ${_BASHUNIT_COVERAGE_NONEXEC_PATTERN} " ' |^[[:space:]]*\)[[:space:]]*(#.*)?$'
426439
427440# Check if a line is executable (used by get_executable_lines and report_lcov)
428441# Arguments: line content, line number
@@ -434,29 +447,11 @@ function bashunit::coverage::is_executable_line() {
434447 # Unused but kept for API compatibility
435448 : " $lineno "
436449
437- # Skip empty lines (line with only whitespace)
450+ # Skip empty lines (line with only whitespace) — built-in, no subshell
438451 [ -z " ${line// / } " ] && return 1
439452
440- # Skip comment-only lines (including shebang)
441- [ " $( echo " $line " | " $GREP " -cE ' ^[[:space:]]*#' || true) " -gt 0 ] && return 1
442-
443- # Skip function declaration lines (but not single-line functions with body)
444- [ " $( echo " $line " | " $GREP " -cE " $_BASHUNIT_COVERAGE_FUNC_PATTERN " || true) " -gt 0 ] && return 1
445-
446- # Skip lines with only braces
447- [ " $( echo " $line " | " $GREP " -cE ' ^[[:space:]]*[\{\}][[:space:]]*$' || true) " -gt 0 ] && return 1
448-
449- # Skip control flow keywords (then, else, fi, do, done, esac, in, ;;, ;&, ;;&)
450- [ " $( echo " $line " | " $GREP " -cE ' ^[[:space:]]*(then|else|fi|do|done|esac|in|;;|;;&|;&)[[:space:]]*(#.*)?$' || true) " -gt 0 ] && return 1
451-
452- # Skip loop terminator with trailing redirection/pipe/fd (e.g. "done < file", "done | sort", "done 2>&1", "done &")
453- [ " $( echo " $line " | " $GREP " -cE ' ^[[:space:]]*done[[:space:]]+[^[:space:]#].*$' || true) " -gt 0 ] && return 1
454-
455- # Skip case patterns like "--option)" or "*) # comment"
456- [ " $( echo " $line " | " $GREP " -cE ' ^[[:space:]]*[^\)]+\)[[:space:]]*(#.*)?$' || true) " -gt 0 ] && return 1
457-
458- # Skip standalone ) for arrays/subshells
459- [ " $( echo " $line " | " $GREP " -cE ' ^[[:space:]]*\)[[:space:]]*(#.*)?$' || true) " -gt 0 ] && return 1
453+ # Single combined grep covers every non-executable pattern
454+ [ " $( echo " $line " | " $GREP " -cE " $_BASHUNIT_COVERAGE_NONEXEC_PATTERN " || true) " -gt 0 ] && return 1
460455
461456 return 0
462457}
0 commit comments