diff --git a/src/error.rs b/src/error.rs index 2fbcd938e1..50c870fcad 100644 --- a/src/error.rs +++ b/src/error.rs @@ -190,6 +190,10 @@ pub(crate) enum Error<'src> { Interrupted { signal: Signal, }, + InvalidShebang { + recipe: Name<'src>, + shebang: String, + }, ListInStringContext { context: StringContext<'src>, value: Value, @@ -817,6 +821,9 @@ impl ColorDisplay for Error<'_> { Interrupted { signal } => { write!(f, "interrupted by {signal}")?; } + InvalidShebang { recipe, shebang } => { + write!(f, "recipe `{recipe}` has invalid shebang `{shebang}`")?; + } ListInStringContext { context, value, .. } => { write!(f, "list value {} {context}", value.color_display(color))?; diff --git a/src/recipe.rs b/src/recipe.rs index fb06185f0b..9ffdb68f5f 100644 --- a/src/recipe.rs +++ b/src/recipe.rs @@ -494,14 +494,11 @@ impl<'src> Recipe<'src> { .unwrap_or_else(|| Interpreter::default_script_interpreter().clone()), ) } else if self.body.first().is_some_and(Line::is_shebang) { - let line = evaluated_lines - .first() - .ok_or_else(|| Error::internal("evaluated_lines was empty"))?; - - let shebang = - Shebang::new(line).ok_or_else(|| Error::internal(format!("bad shebang line: {line}")))?; - - Executor::Shebang(shebang) + let shebang = &evaluated_lines[0]; + Executor::Shebang(Shebang::new(shebang).ok_or_else(|| Error::InvalidShebang { + recipe: self.name, + shebang: shebang.into(), + })?) } else { Executor::Command( context diff --git a/tests/shebang.rs b/tests/shebang.rs index b420a5dbdd..844dab946d 100644 --- a/tests/shebang.rs +++ b/tests/shebang.rs @@ -169,3 +169,17 @@ fn run_shebang() { .stderr("error: recipe `a` failed with exit code 200\n") .status(200); } + +#[test] +fn invalid_shebang_error() { + Test::new() + .justfile( + " + foo: + #!{{ '' }} + echo bar + ", + ) + .stderr_regex("error: recipe `foo` has invalid shebang `#!`\n") + .failure(); +}