You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
SC2218 false positive: re-source-ing a library file in a later @test block flags an earlier, correctly-defined function call as "only defined later" #3509
In a .bats test file, setup()sources a library file once, defining (among others) a function _bo_parse. The very first @test block calls _bo_parse — at that point it has unambiguously already been defined, by setup(), which bats runs before every test. A later, separate@test block re-sources the same library file mid-body (e.g. to layer extra fixture state on top of setup()'s). That later, unrelated re-source makes ShellCheck retroactively flag the earlier test's call to _bo_parse as SC2218 ("This function is only defined later. Move the definition up."), even though nothing about that earlier test changed.
Removing the later re-source call makes the false positive disappear without changing the flagged test at all.
Reproduction
This is a real project's library (betteropts.sh, ~1069 lines, plain bash, no bashisms beyond ordinary functions/arrays), sourced as-is. I was not able to shrink it down to a small synthetic stand-in — a small hand-written library with the same shape (a handful of functions, one defined well after the others, sourced twice across two @test blocks) did not reproduce this, so whatever triggers it seems tied to some property of a larger/more complex sourced file, not just "any function sourced twice." I'm filing the observed case as-is rather than guessing at that mechanism.
#!/usr/bin/env batssetup() {
# shellcheck source=./betteropts.shsource"$BATS_TEST_DIRNAME/betteropts.sh"
flag verbose -v --verbose
}
@test "long flag is marked provided" {
_bo_parse --verbose
}
@test "a later test re-sources betteropts.sh mid-body" {
# shellcheck source=./betteropts.shsource"$BATS_TEST_DIRNAME/betteropts.sh"
argument files variadic
}
Run: shellcheck -x t.bats
In t.bats line 10:
_bo_parse --verbose
^-----------------^ SC2218 (error): This function is only defined later. Move the definition up.
Expected
No warning. _bo_parse is defined in setup(), which runs before every @test block including the first one; nothing about the second, unrelated @test block's re-source should retroactively affect analysis of the first.
What makes it go away
Deleting the second @test block's source line entirely (or deleting the whole second @test block) makes the warning disappear with nothing else changed:
#!/usr/bin/env batssetup() {
# shellcheck source=./betteropts.shsource"$BATS_TEST_DIRNAME/betteropts.sh"
flag verbose -v --verbose
}
@test "long flag is marked provided" {
_bo_parse --verbose
}
shellcheck -x t.bats → clean, exit 0.
Notes
Found via bisection on a real ~250-line .bats file where 15 call sites across many @test blocks were all flagged as SC2218 for functions (_bo_parse, _bo_assign_positionals) that are unambiguously defined by a source in setup(). Removing 11 mid-body re-source calls scattered through later tests (added to layer extra fixture state onto the shared setup()) dropped the count from 15 to 0 with no other change.
ShellCheck version: 0.11.0 (
shellcheck --version)Summary
In a
.batstest file,setup()sources a library file once, defining (among others) a function_bo_parse. The very first@testblock calls_bo_parse— at that point it has unambiguously already been defined, bysetup(), which bats runs before every test. A later, separate@testblock re-sources the same library file mid-body (e.g. to layer extra fixture state on top ofsetup()'s). That later, unrelated re-sourcemakes ShellCheck retroactively flag the earlier test's call to_bo_parseas SC2218 ("This function is only defined later. Move the definition up."), even though nothing about that earlier test changed.Removing the later re-
sourcecall makes the false positive disappear without changing the flagged test at all.Reproduction
This is a real project's library (
betteropts.sh, ~1069 lines, plain bash, no bashisms beyond ordinary functions/arrays), sourced as-is. I was not able to shrink it down to a small synthetic stand-in — a small hand-written library with the same shape (a handful of functions, one defined well after the others,sourced twice across two@testblocks) did not reproduce this, so whatever triggers it seems tied to some property of a larger/more complex sourced file, not just "any functionsourced twice." I'm filing the observed case as-is rather than guessing at that mechanism.Library: https://raw.githubusercontent.com/VirtusLab/betteropts/5be1b58f92287045f6d61d817f22b4f564ecd302/betteropts.sh
(save as
betteropts.shnext to the test file below)t.bats:Run:
shellcheck -x t.batsExpected
No warning.
_bo_parseis defined insetup(), which runs before every@testblock including the first one; nothing about the second, unrelated@testblock's re-sourceshould retroactively affect analysis of the first.What makes it go away
Deleting the second
@testblock'ssourceline entirely (or deleting the whole second@testblock) makes the warning disappear with nothing else changed:shellcheck -x t.bats→ clean, exit 0.Notes
.batsfile where 15 call sites across many@testblocks were all flagged as SC2218 for functions (_bo_parse,_bo_assign_positionals) that are unambiguously defined by asourceinsetup(). Removing 11 mid-body re-sourcecalls scattered through later tests (added to layer extra fixture state onto the sharedsetup()) dropped the count from 15 to 0 with no other change.@testblocks in a.batsfile), but the reported check here is SC2218, a different check with presumably different internal logic, so I'm filing it separately rather than assuming it shares a fix.